欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ExcelUtil

程序员文章站 2022-06-22 17:09:33
...

说明:数据库使用mysql,导入的字段类型只能是有varchar,Date 类型

火狐导出文件时做特殊设置,不能用url编码

源代码参考附件:

 

ExcelUtil.java:  导入核心代码

package com.lw.util;


import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FilenameUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

/**
 * Excel组件
 * 
 * @author liwei
 * @version 1.0
 * @since 1.0
 */
public class ExcelUtil {

	/**
	 * Excel 2003
	 */
	private final static String XLS = "xls";
	/**
	 * Excel 2007
	 */
	private final static String XLSX = "xlsx";

	
	public static void main(String[] args) {
		String file = "d://importStudentDatas.xls";
		String[] columnArray = new String[]{"code", "name", "birthDate"};
		Map<String, ColumnCheckTypeEnum> checkColumn = new HashMap<String, ColumnCheckTypeEnum>();
		checkColumn.put("code", ColumnCheckTypeEnum.CHECK_TYPE_PRICE_1);

		try {
			List<Student> list = ExcelUtil.excelToList(new File(file), 0, Student.class, columnArray, checkColumn);
			System.out.println(list.size());
			for (int i = 0; i < list.size(); i++) {
				Student stu = list.get(i);
				System.out.println("编码:" + stu.getCode() + " >>>>> 名称:" + stu.getName() + " >>>>> 生日:" + stu.getBirthDate());
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (ParseException e) {
			e.printStackTrace();
		}
		
	}

	
	/***
	 * 导入Excel数据
	 * 
	 * <li>1、读取excel数据</li>
	 * <li>2、校验数据的合法性(日期,金额,字符长度(和数据库结构比较))</li>
	 * <li>3、合法数据绑定到bean对象中(反射)</li>
	 * <li>4、得到数据层面校验通过的bean对象集合,</li>
	 * @param file 导入数据文件
	 * @param entityClass bean对象类型bean.class
	 * @param sheetIndex sheet索引
	 * @param columnArray 字段列数组 (需要导入的字段数组)
	 * @param checkColumn 需要校验格式的字段列Map
	 * @throws IOException
	 * @throws NoSuchFieldException 
	 * @throws SecurityException 
	 * @throws InstantiationException 
	 * @throws SQLException 
	 * @throws IllegalAccessException 
	 * @throws ParseException 
	 */
	public static <T> List<T> excelToList(File file, Integer sheetIndex, Class<T> entityClass, String[] columnArray, Map<String, ColumnCheckTypeEnum> checkColumn) throws IOException, SecurityException, NoSuchFieldException, InstantiationException, SQLException, IllegalAccessException, ParseException {
		List<T> list = new ArrayList<T>();
		Workbook workbook = null;
		if (XLS.equalsIgnoreCase(FilenameUtils.getExtension(file.getName()))) {
			workbook = new HSSFWorkbook(new FileInputStream(file));
		} else if (XLSX.equalsIgnoreCase(FilenameUtils.getExtension(file.getName()))) {
			workbook = new XSSFWorkbook(new FileInputStream(file));
		} else {
			throw new IOException("导入excel出错,不支持文件类型!");
		}
		if(sheetIndex==null) {
			sheetIndex = 0;
		}
		if((sheetIndex+1)>workbook.getNumberOfSheets()) {
			throw new IndexOutOfBoundsException("导入excel出错,指定sheet索引越界!");
		}
		// sheet中要导出的列
		if(columnArray==null || columnArray.length<1) {
			throw new NullPointerException("导入excel出错,导入列设置错误!");
		}
		
		// 拿到sheet
		Sheet sheet = workbook.getSheetAt(sheetIndex);
		String sheetName = sheet.getSheetName(); // sheetName 使用表名称
		
		// 解析公式结果
		FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
		// 每个sheet中的数据
		List<Map<String, Object>> dataList = readSheet(sheet, evaluator, columnArray);
		
		// 数据库表对应的字段列信息
		List<ColumnData> columnDatas = null;
		try {
			columnDatas = ProDataUtil.getMySqlColumnDatas(sheetName, "scp");
		} catch (SQLException e) {
			throw new SQLException("导入excel出错,获取表信息错误!");
		}
		// 遍历数据库表对应的字段列信息
		for(int j=0; j<dataList.size(); j++) {
			// 拿到每一行的数据
			Map<String, Object> rowData = dataList.get(j);
			T o = null;
			try {
				o = (T) entityClass.newInstance();
			} catch (IllegalAccessException e) {
				throw new IllegalAccessException("导入excel出错,错误信息:" + e.getMessage());
			}
			for(int k=0; k<columnDatas.size(); k++) {
				ColumnData cd = columnDatas.get(k);
				Object value = rowData.get(cd.getColumnLowName());

				String errMsg = "导入excel出错,错误位置>>:sheet【" + sheetName + "】中,第【"+ (j+1+1) +"】行,第【" + (getIndexOfArrayItem(cd.getColumnLowName(), columnArray) + 1 + 1) + "】列。错误信息:";
				// 1、先根据设置,校验自定义校验的列
				if(checkColumn.containsKey(cd.getColumnLowName())==false || ColumnCheckTypeEnum.CHECK_TYPE_NOTNULL.equals(checkColumn.get(cd.getColumnLowName()))==false) {
					if(value==null) {
						continue;
					}
				}
				ColumnCheckTypeEnum checkType = checkColumn.get(cd.getColumnLowName());
				// 非空校验
				if(ColumnCheckTypeEnum.CHECK_TYPE_NOTNULL.equals(checkType)) {
					if(value==null) {
						throw new InputMismatchException(errMsg + "此列为非空列,请检查excel数据是否为空!");
					}
				}
				// 整数类型校验
				else if(ColumnCheckTypeEnum.CHECK_TYPE_LONG.equals(checkType)) {
					try {
						Long.parseLong(value.toString());
					} catch (NumberFormatException e) {
						throw new NumberFormatException(errMsg + "此列为整数列,单元格应设置为文本类型,请检查excel数据是否整数数字或单元格是否为文本类型!" + e.getMessage());
					}
				}
				// 价格类型校验,取2位小数
				else if(ColumnCheckTypeEnum.CHECK_TYPE_PRICE.equals(checkType)) {
					try {
						String price = value.toString();
						new BigDecimal(price);
						if(price.indexOf(".")>-1 && price.split("\\.")[1].length()>2) {
							throw new InputMismatchException(errMsg + "此列为金额数值列,小数点后不超过2位小数,请检查excel数据是否合格!");
						}
					} catch (NumberFormatException e) {
						throw new NumberFormatException(errMsg + "此列为金额数值列,请检查excel数据是否合格!");
					}
				}
				// 价格类型校验,取4位小数
				else if(ColumnCheckTypeEnum.CHECK_TYPE_PRICE_1.equals(checkType)) {
					try {
						String price = value.toString();
						new BigDecimal(price);
						if(price.indexOf(".")>-1 && price.split("\\.")[1].length()>4) {
							throw new InputMismatchException(errMsg + "此列为金额数值列,小数点后不超过4位小数,请检查excel数据是否合格!");
						}
					} catch (NumberFormatException e) {
						throw new NumberFormatException(errMsg + "此列为金额数值列,请检查excel数据是否合格!");
					}
				}
				
				// 2、自定义校验完成后,进行常规校验
				// 通过属性名称获取属性,把值设置到属性里面
				Field field = entityClass.getDeclaredField(cd.getColumnLowName());
				field.setAccessible(true); // 设置属性可访问, private
				if("java.lang.String".equalsIgnoreCase(cd.getJavaType())) {
					if(value.toString().length()>Integer.parseInt(cd.getDataMaxLength())) {
						throw new SQLException(errMsg + "字符长度超过数据库设置长度限制!数据库长度【"+cd.getDataMaxLength()+"】,当前文本长度【"+value.toString().length()+"】");
					}
					try {
						field.set(o, value.toString());
					} catch (IllegalArgumentException e) {
						throw new IllegalArgumentException(errMsg + e.getMessage());
					} catch (IllegalAccessException e) {
						throw new IllegalAccessException(errMsg + e.getMessage());
					}
				} else if("java.util.Date".equalsIgnoreCase(cd.getJavaType())) {
					try {
						field.set(o, (Date)value);
					} catch (ClassCastException e) {
						throw new ClassCastException(errMsg + "数据格式错误,无法将【"+value+"】转换为java.util.Date类型日期格式。" + e.getMessage());
					} catch (IllegalArgumentException e) {
						throw new IllegalArgumentException(errMsg + e.getMessage());
					} catch (IllegalAccessException e) {
						throw new IllegalAccessException(errMsg + e.getMessage());
					}
				} else if("java.lang.Integer".equalsIgnoreCase(cd.getJavaType())) {
					try {
//							field.setInt(o, Integer.parseInt(value.toString())); // Int to Integer 赋值时类型不匹配
						field.set(o, Integer.parseInt(value.toString()));
					} catch (NumberFormatException e) {
						throw new NumberFormatException(errMsg + "数据格式错误,无法将【"+value+"】转换为java.lang.Integer类型。" + e.getMessage());
					} catch (IllegalArgumentException e) {
						throw new IllegalArgumentException(errMsg + e.getMessage());
					} catch (IllegalAccessException e) {
						throw new IllegalAccessException(errMsg + e.getMessage());
					}
				}
			}
			list.add(o);
		}
		return list;
	}
	
	/***
	 * 读取单个sheet
	 * <p>导入Excel数据使用私有方法</p>
	 * @param sheet 单个sheet
	 * @param evaluator 解析公式结果
	 * @param columnArray 字段列数组
	 */
	private static List<Map<String, Object>> readSheet(Sheet sheet, FormulaEvaluator evaluator, String... columnArray) {
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		int firstRowNum = sheet.getFirstRowNum();
		int lastRowNum = sheet.getLastRowNum();
		// 从第二行开始读取数据
		for (int rowIndex = firstRowNum + 1; rowIndex <= lastRowNum; rowIndex++) {
			Row row = sheet.getRow(rowIndex);
			short firstCellNum = row.getFirstCellNum();
			// short maxColIx = row.getLastCellNum();
			Map<String, Object> rowMap = new HashMap<String, Object>();
			// 读取列的时候,按照设置好的字段列的数量长度循环读取
			for (short colIndex = firstCellNum; colIndex < columnArray.length; colIndex++) {
				Cell cell = row.getCell(new Integer(colIndex)+1); // 从第二列开始导入, 第一列序号列不做导入
				CellValue cellValue = evaluator.evaluate(cell);
				if (cellValue == null) {
					continue;
				}
				// 经过公式解析,最后只存在Boolean、Numeric和String三种数据类型,此外就是Error了
				// 其余数据类型,根据官方文档,完全可以忽略http://poi.apache.org/spreadsheet/eval.html
				switch (cellValue.getCellType()) {
					case Cell.CELL_TYPE_BOOLEAN:
						rowMap.put(columnArray[colIndex], cellValue.getBooleanValue());
						break;
					case Cell.CELL_TYPE_NUMERIC:
						// 这里的日期类型会被转换为数字类型,需要判别后区分处理
						if (HSSFDateUtil.isCellDateFormatted(cell)) {
							rowMap.put(columnArray[colIndex], cell.getDateCellValue());
						} else {
							rowMap.put(columnArray[colIndex], cellValue.getNumberValue());
						}
						break;
					case Cell.CELL_TYPE_STRING:
						rowMap.put(columnArray[colIndex], cellValue.getStringValue());
						break;
					case Cell.CELL_TYPE_FORMULA:
						break;
					case Cell.CELL_TYPE_BLANK:
						break;
					case Cell.CELL_TYPE_ERROR:
						break;
					default:
						break;
				}
			}
			list.add(rowMap);
		}
		
		return list;
	}
	
	private static int getIndexOfArrayItem(String arrItem, String[] array) {
		int index = -1;
		for (int i = 0; i < array.length; i++) {
			if(array[i].equals(arrItem)) {
				index = i;
				break;
			}
		}
		return index;
	}
	
	/***
	 * 导出Excel数据
	 * @param sheetName sheet名称
	 * @param fileName 导出文件名
	 * @param list 要导出的数据
	 * @param response
	 * @throws IOException
	 */
	public static void exportDatas(String sheetName, String fileName, List<Map<String, Object>> list, HttpServletRequest request, HttpServletResponse response) throws IOException {
		HSSFWorkbook wb = new HSSFWorkbook();
		HSSFSheet sheet = wb.createSheet(sheetName);
		int rowNum = 0;

		// 设置单元格样式(标题样式)
		HSSFCellStyle styleTitle = wb.createCellStyle();
		styleTitle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
		styleTitle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
		styleTitle.setBorderRight(HSSFCellStyle.BORDER_THIN);
		styleTitle.setBorderTop(HSSFCellStyle.BORDER_THIN);
		// 对齐方式
		styleTitle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
		styleTitle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
		// 字体
		HSSFFont font = wb.createFont();
		font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
		styleTitle.setFont(font);
		
		// 设置单元格样式(数据值样式)
		HSSFCellStyle styleContent = wb.createCellStyle();
		styleContent.setBorderBottom(HSSFCellStyle.BORDER_THIN);
		styleContent.setBorderLeft(HSSFCellStyle.BORDER_THIN);
		styleContent.setBorderRight(HSSFCellStyle.BORDER_THIN);
		styleContent.setBorderTop(HSSFCellStyle.BORDER_THIN);
		styleContent.setAlignment(HSSFCellStyle.ALIGN_CENTER);
		
		// 标题行
		HSSFRow rowTitle = sheet.createRow(rowNum++);
		
		int i = 0;
		HSSFCell cellH0 = rowTitle.createCell(i++);
		cellH0.setCellValue("序号");
		cellH0.setCellStyle(styleTitle);
		for(Map.Entry<String, Object> tmp : list.get(0).entrySet()) {
			HSSFCell cellH = rowTitle.createCell(i++);
			cellH.setCellValue(tmp.getKey());
			cellH.setCellStyle(styleTitle);
		}

		int cellOrder = 1;
		for (Map<String, Object> map : list) {
			HSSFRow row = sheet.createRow(rowNum++);
			int j = 0;
			// 序号
			HSSFCell cellC0 = row.createCell(j++);
			cellC0.setCellValue(cellOrder++);
			cellC0.setCellStyle(styleContent);
			
			for(Map.Entry<String, Object> entry : map.entrySet()) {
				HSSFCell cellC = row.createCell(j++);
				sheet.setColumnWidth(cellC.getColumnIndex(), 256 * (11 + 10));
				cellC.setCellValue(entry.getValue()+"");
				cellC.setCellStyle(styleContent);
			}
		}
		
		String agent = request.getHeader("User-Agent");
		// 火狐浏览器导出文件不会url解码
		if(StringUtil.isEmpty(agent)==false && agent.toLowerCase().indexOf("firefox")>-1) {
			response.setHeader("Content-disposition", "attachment;filename=\"" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1")+".xls\"");
		} else {
			response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName+".xls", "UTF-8"));
		}
		response.setContentType("application/vnd.ms-excel;charset=UTF-8");
		OutputStream out = response.getOutputStream();
		wb.write(out);
		out.flush();
		out.close();
	}
	
	
}

 

ProDataUtil.java  获取数据库表信息

package com.lw.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class ProDataUtil {

	private static String url = "jdbc:mysql://192.168.1.1:3306/dbName?characterEncoding=utf8&amp;zeroDateTimeBehavior=convertToNull";
	private static String username = "root";
	private static String password = "root";
	private static String driverClassName = "com.mysql.jdbc.Driver";

	/**
	 * 查询表的字段,封装成List
	 * 
	 * @param tableName 表名
	 * @param schemaName 数据库名
	 * @return
	 * @throws SQLException
	 */
	public static List<ColumnData> getMySqlColumnDatas(String tableName, String schemaName) throws SQLException {
		String SQLColumns = " select COLUMN_NAME,DATA_TYPE,column_comment,numeric_scale,numeric_precision,character_maximum_length,ordinal_position from information_schema.COLUMNS where table_name =  '" + tableName + "'" + " and TABLE_SCHEMA='" + schemaName + "' " + " ORDER by  ordinal_position";
		Connection con = getConnection();
		PreparedStatement ps = con.prepareStatement(SQLColumns);
		List<ColumnData> columnList = new ArrayList<ColumnData>();
		ResultSet rs = ps.executeQuery();
		while (rs.next()) {
			String name = rs.getString(1);
			String lowName = getColumnBeanName(name); // 将XXX_XXX转换成xxxXxx的格式
			String type = rs.getString(2).toUpperCase(); // mysql 区分大小写
			String javaType = getType(rs.getString(2), rs.getString(4), rs.getString(5)); // mysql 区分大小写
			String comment = rs.getString(3);
			String dataScale = rs.getString(4);
			String dataPrecision = rs.getString(5);
			String dataMaxLength = rs.getString(6);
			
			ColumnData cd = new ColumnData(name, lowName, type, javaType, comment, dataScale, dataPrecision, dataMaxLength);
			columnList.add(cd);
		}
		rs.close();
		ps.close();
		con.close();
		return columnList;
	}

	public static Connection getConnection() throws SQLException {
		try{
			Class.forName(driverClassName);
		}catch(Exception e){
			e.printStackTrace();
		}
		return DriverManager.getConnection(url, username, password);
	}

	private static String getColumnBeanName(String column) {
		String[] split = column.split("_");
		StringBuffer columnVal = new StringBuffer();
		if (split.length > 1) {
			for (int i = 0; i < split.length; i++) {
				String colVal = "";
				if (i == 0) {
					colVal = split[i].toLowerCase();
					columnVal.append(colVal);
				} else {
					colVal = split[i].substring(0, 1).toUpperCase() + split[i].substring(1, split[i].length()).toLowerCase();
					columnVal.append(colVal);
				}
			}
			columnVal.toString();
		} else {
			String colVal = column.toLowerCase();
			columnVal.append(colVal);
		}
		return columnVal.toString();
	}

	/***
	 * 获取java类型
	 * @param type 数据库数据类型
	 * @param dataScale 小数位数
	 * @param dataPrecision 数据精度
	 * @return
	 */
    private static String getType(String type, String dataScale, String dataPrecision){
    	type=type.toLowerCase();
    	if("char".equalsIgnoreCase(type) || "varchar".equalsIgnoreCase(type)|| "varchar2".equalsIgnoreCase(type)){
			return "java.lang.String";
		}else if("NUMBER".equalsIgnoreCase(type)|| "numeric".equalsIgnoreCase(type)){//
			if((dataScale!=null&&!dataScale.equals("")&&!dataScale.equals("0"))){
				if(dataPrecision!=null&&dataPrecision.equals("38")){
					return "java.math.BigDecimal";
				}else{
					return "java.lang.Double";
				}
			}else {
				if(dataPrecision!=null&&dataPrecision.equals("38")){
					return "java.math.BigDecimal";
				}else{
					return "java.lang.Long";
				}
			}
		}else if("decimal".equalsIgnoreCase(type)){//
			return "java.math.BigDecimal";
		}else if( "DATE".equalsIgnoreCase(type)){
			return "java.util.Date";
		}else if( "DATETIME".equalsIgnoreCase(type)){
			return "java.util.Date";
		}else if( "BLOB".equalsIgnoreCase(type)){
			return "java.sql.Blob";
		}else if( "CLOB".equalsIgnoreCase(type)){
			return "java.sql.Clob";
		}else if("int".equalsIgnoreCase(type)){
			return "java.lang.Integer";
		}else if("TINYINT".equalsIgnoreCase(type)){
			return "java.lang.Boolean";
		}else if("double".equalsIgnoreCase(type)){
			return "java.math.BigDecimal";
		}else if("datetime".equalsIgnoreCase(type)){
			return "java.util.Date";
		}
    	return null;
    }

	
}