SSM 使用 POI 组件做 Excle 的批量导入导出

本贴最后更新于 1798 天前,其中的信息可能已经事过景迁

批量导入使用的场景挺多的,自己毕设也涉及到了,做个记录。
导包在上一篇里已经提到了,这里就略过,重点记录一下实现的过程。

JSP 界面

这个部分和上传 word 的一样,就是一个 form

<form class="form-horizontal" id="fm_batchImport" method="post" enctype="multipart/form-data">
	<input id="batchImport" name="enclosure" type="file" 
		accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel">
	<a href="studentTemplate">模板下载</a>
	<button type="button" onClick="saveBatchImport()">提交</button>
</form>

还是和 word 一样,这里稍微做一个 accept 的验证,其实然并卵,后台还是需要判断。然后还是采用的 Ajax 上传,弄成 formData。

Controller

两个方法,一个是作为导入,一个响应下载导入模板。

//批量导入学生信息
@RequestMapping(value="/batchImport",method=RequestMethod.POST)
@ResponseBody
public Integer batchImport(@ModelAttribute MultipartFile file, HttpServletRequest request, HttpServletResponse response) throws Exception {
	int result = studentService.batchImportByAdmin(file, request, response);
	return result;
}

//模板下载
@RequestMapping("/studentTemplate")
@ResponseBody
public void studentTemplate(HttpServletRequest request, HttpServletResponse response) {
	// excel标题
	String[] title = { "学历名称", "专业名称", "学生姓名", "入学时间(yyyy-MM-dd)" };
	// excel文件名
	String fileName = "批量导入学生信息模板" + System.currentTimeMillis() + ".xls";
	// sheet名
	String sheetName = "学生模板";
	String[][] content = new String[0][title.length];
	//创建HSSFWorkbook 
	HSSFWorkbook wb = ExcelUtil.getHSSFWorkbook(sheetName, title, content, null);
	// 响应到客户端
	try {
		this.setResponseHeader(response, fileName);
		OutputStream os = response.getOutputStream();
		wb.write(os);
		os.flush();
		os.close();
	} catch (Exception e) {
		e.printStackTrace();
	}
}
private void setResponseHeader(HttpServletResponse response, String fileName) {
	try {
		try {
			fileName = new String(fileName.getBytes(), "ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
		}
		response.setContentType("application/octet-stream;charset=ISO-8859-1");
		response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
		response.addHeader("Pargam", "no-cache");
		response.addHeader("Cache-Control", "no-cache");
	} catch (Exception ex) {
		ex.printStackTrace();
	}
}

用到一个工具类 ExcelUtil

package com.shms.util;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
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;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.ss.usermodel.HorizontalAlignment;

public class ExcelUtil {

	private final static String EXCEL2003L = ".xls"; // 2003- 版本的excel
	private final static String EXCEL2007U = ".xlsx"; // 2007+ 版本的excel

	/**
	 * 导出Excel
	 * 
	 * @param sheetName sheet名称
	 * @param title     标题
	 * @param values    内容
	 * @param wb        HSSFWorkbook对象
	 * @return
	 */
	public static HSSFWorkbook getHSSFWorkbook(String sheetName, String[] title, String[][] values, HSSFWorkbook wb) {
		// 第一步,创建一个HSSFWorkbook,对应一个Excel文件
		if (wb == null) {
			wb = new HSSFWorkbook();
		}
		// 第二步,在workbook中添加一个sheet,对应Excel文件中的sheet
		HSSFSheet sheet = wb.createSheet(sheetName);
		// 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制
		HSSFRow row = sheet.createRow(0);
		// 第四步,创建单元格,并设置表头 设置表头居中
		HSSFCellStyle style = wb.createCellStyle();
		style.setAlignment(HorizontalAlignment.CENTER); // 创建一个居中格式
		// 声明列对象
		HSSFCell cell = null;
		// 创建标题
		for (int i = 0; i < title.length; i++) {
			cell = row.createCell(i);
			cell.setCellValue(title[i]);
			cell.setCellStyle(style);
		}
		// 创建内容
		for (int i = 0; i < values.length; i++) {
			row = sheet.createRow(i + 1);
			for (int j = 0; j < values[i].length; j++) {
				// 将内容按顺序赋给对应的列对象
				row.createCell(j).setCellValue(values[i][j]);
			}
		}
		return wb;
	}

	/**
	 * 导入excel 描述:获取IO流中的数据,组装成List<List<Object>>对象
	 * 
	 * @param in,fileName
	 * @return
	 * @throws IOException
	 */
	public List<List<Object>> getBankListByExcel(InputStream in, String fileName) throws Exception {
		List<List<Object>> list = null;

		// 创建Excel工作薄
		Workbook work = this.getWorkbook(in, fileName);
		if (null == work) {
			throw new Exception("创建Excel工作薄为空!");
		}
		Sheet sheet = null; // Sheet页数
		Row row = null; // 行数
		Cell cell = null; // 列数

		list = new ArrayList<List<Object>>();
		// 遍历Excel中所有的sheet
		// 将最大的列数记录下来
		int lastCellNum = 0;
		for (int i = 0; i < work.getNumberOfSheets(); i++) {
			sheet = work.getSheetAt(i);
			if (sheet == null) {
				continue;
			}
			// 遍历当前sheet中的所有行
			for (int j = sheet.getFirstRowNum(); j <= sheet.getLastRowNum(); j++) {
				row = sheet.getRow(j);
				if (row == null || row.getFirstCellNum() == j) {
					continue;
				}
				// 遍历所有的列
				List<Object> li = new ArrayList<Object>();
				// 比较当前行的列数跟表的最大的列数
				if (j == sheet.getFirstRowNum()) {
					// 将第一行的列数设为最大
					lastCellNum = row.getLastCellNum();
				} else {
					lastCellNum = lastCellNum > row.getLastCellNum() ? lastCellNum : row.getLastCellNum();
				}
				for (int y = row.getFirstCellNum(); y < lastCellNum; y++) {
					cell = row.getCell(y);
					li.add(this.getValue(cell));
				}
				list.add(li);
			}
		}
		return list;
	}

	/**
	 * 描述:根据文件后缀,自适应上传文件的版本
	 * 
	 * @param inStr,fileName
	 * @return
	 * @throws Exception
	 */
	public Workbook getWorkbook(InputStream inStr, String fileName) throws Exception {
		Workbook wb = null;
		String fileType = fileName.substring(fileName.lastIndexOf("."));
		if (EXCEL2003L.equals(fileType)) {
			wb = new HSSFWorkbook(inStr); // 2003-
		} else if (EXCEL2007U.equals(fileType)) {
			wb = new XSSFWorkbook(inStr); // 2007+
		} else {
			throw new Exception("解析的文件格式有误!");
		}
		return wb;
	}

	/**
	 * 描述:对表格中数值进行格式化
	 * 
	 * @param cell
	 * @return
	 */
	// 解决excel类型问题,获得数值
	public String getValue(Cell cell) {
		String value = "";
		if (null == cell) {
			return value;
		}
		switch (cell.getCellType()) {
		// 数值型
		case NUMERIC:
			if (HSSFDateUtil.isCellDateFormatted(cell)) {
				// 如果是date类型则 ,获取该cell的date值
				Date date = HSSFDateUtil.getJavaDate(cell.getNumericCellValue());
				// 根据自己的实际情况,excel表中的时间格式是yyyy-MM-dd HH:mm:ss还是yyyy-MM-dd,或者其他类型
				SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
				// 由于方法的返回值类型为String,这里将Date类型转为String,便于统一返回数据
				value = format.format(date);
			} else {// 纯数字
				BigDecimal big = new BigDecimal(cell.getNumericCellValue());
				value = big.toString();
				// 解决1234.0 去掉后面的.0
				if (null != value && !"".equals(value.trim())) {
					String[] item = value.split("[.]");
					if (1 < item.length && "0".equals(item[1])) {
						value = item[0];
					}
				}
			}
			break;
		// 字符串类型
		case STRING:
			value = cell.getStringCellValue().toString();
			break;
		// 公式类型
		case FORMULA:
			// 读公式计算值
			value = String.valueOf(cell.getNumericCellValue());
			if (value.equals("NaN")) {// 如果获取的数据值为非法值,则转换为获取字符串
				value = cell.getStringCellValue().toString();
			}
			break;
		// 布尔类型
		case BOOLEAN:
			value = " " + cell.getBooleanCellValue();
			break;
		default:
			value = cell.getStringCellValue().toString();
		}
		if ("null".endsWith(value.trim())) {
			value = "";
		}
		return value;
	}
}

在工具类中,有一点需要注意,cell.getCellType()的枚举书写,在 4.x 以前,是 Cell.CELL_TYPE_STRING 这种形式,在 4.0.改成了 CellType.STRING,在最新版中,变成了 STRING
其实模板下载,也可以作为后台数据导出 Excel,只需要下载模板里边稍微改一下就行。比如导出成绩表

	String[][] content = new String[scoreList.size()][title.length]
	for (int i = 0; i < scoreList.size(); i++) {
		ScoreWrapper score = scoreList.get(i);
		content[i][0] = score.getcId();
		content[i][1] = score.getcName();
		content[i][2] = score.gettId();
		content[i][3] = score.gettName();
		content[i][4] = score.getsId();
		content[i][5] = score.getsName();
		content[i][6] = score.getAvgSubScore().toString();
	}

这样,也就把导出的功能完成了。

Service

具体的逻辑处理部分,读取表格数据。

@Override
public Integer batchImportByAdmin(MultipartFile file, HttpServletRequest request, HttpServletResponse response) throws Exception{
	if (file.isEmpty()) {
		try {
			throw new Exception("文件不存在!");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	//这里可以使用String originalFilename = file.getOriginalFilename();
	//originalFilename.endsWith(".xls") || originalFilename.endsWith(".xlsx")
	//做一个后缀名的判断,从而限制前端的文件格式
	InputStream in = null;
	try {
		in = file.getInputStream();
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	List<List<Object>> listob = null; 
	try {
		listob = new ExcelUtil().getBankListByExcel(in,file.getOriginalFilename());
	} catch (Exception e) {
		e.printStackTrace();
	} 

	// 该处可调用mapper相应方法进行数据保存到数据库中
	for (int i = 0; i < listob.size(); i++) {
		List<Object> lo = listob.get(i);
		Student student = new Student();
		//这里可以做一些数据库查询操作等等..

		//这里的lo集合就是Excel表对应的列的数据(第一行默认是表头,不读取)
		String.valueOf(lo.get(0));//表示第i行第一列的数据,以此类推...
		
		//把数据塞入student对象,调用mapper方法进行存储
		student.setXXX();
		
		studentMapper.insertSelective(student);
	}
	return ...;
}

这样就完成了 Excel 的批量导入导出功能。

  • POI
    22 引用 • 21 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...