本着不重复造轮子的开发思想,我们对Excel表格的操作都通过easyExcel进行实现
使用开源轮子实现Excel下载、导入、导出的功能。
一、项目集成
1、导包
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
1、实现已有Excel模板下载
很多系统有数据批量导入的场景,因为在页面上批量加数据时间成本太大了,但是一般导入的时候得按照一定的格式改,所以一般好的产品会先让用户下载一个带有格式的文档,然后按照格式写好以后上传导入
根据自己的项目创建一个Excel模板,放到项目配置文件下
目录结构如下
二、功能实现
1、下载
文件模板已经准备好了,实现下载模板的功能
下载功能代码也很简单,主要分为加载资源->读取资源->写入响应流
实现如下:
/**
* 下载模板
*/
@GetMapping("/downloadTemplate")
public void downloadTemplate(HttpServletResponse response) throws Exception {
//获取模板文件
String fileName = "template_.xlsx";
String templatePath = "excelTemplate/" + fileName;
ClassPathResource classPathResource = new ClassPathResource(templatePath);
InputStream inputStream = classPathResource.getInputStream();
ServletOutputStream servletOutputStream = null;
try {
response.setContentType("application/force-download");
response.setHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes(), StandardCharsets.UTF_8));
servletOutputStream = response.getOutputStream();
IoUtil.copy(inputStream, servletOutputStream);
response.flushBuffer();
} catch (Exception e) {
log.error("下载失败,文件模板{}不存在", fileName);
} finally {
if (servletOutputStream != null) {
servletOutputStream.close();
}
inputStream.close();
}
}
注意:
XSSF操作的是Excel2007以上的版本,对应文件的后缀名是xlsx
Workbook workbook = new XSSFWorkbook(inputStream)
HSSF操作的是Excel2003以前的版本,对应的文件后缀名是xls
Workbook workbook = new HSSFWorkbook(inputStream)
2、导入
将数据导出到文档这种场景可以说是最常见的了,那么怎么使用easyExcel
快速实现呢,我们同样还是以上面的模板为例
定义模型映射对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserExcelModel implements Serializable {
private static final long serialVersionUID = 2723059827514336242L;
@ExcelProperty(value = "用户名", index = 0)
private String name;
@ExcelProperty(value = "年龄", index = 1)
private Integer age;
@ExcelProperty(value = "性别", index = 2)
private String sex;
@ExcelProperty(value = "手机号", index = 3)
private String mobile;
}
注意模型映射对象一定要有默认的无参构造函数
导入数据的主要流程如下:定义列标题->创建sheet->自定义字体和风格->构造数据->写入数据->写入到浏览器响应流
代码实现如下:
/**
* 读取数据
*
* @param file 文件名称
* @return list
*/
@PostMapping("/readExcel")
public List<UserExcelModel> readExcel(@RequestParam("file") MultipartFile file) {
List<UserExcelModel> list = new ArrayList<>();
try {
list = EasyExcel.read(file.getInputStream(), UserExcelModel.class, new ModelExcelListener()).sheet().doReadSync();
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
读取监听
@Slf4j
public class ModelExcelListener extends AnalysisEventListener {
private final List<Object> dataList = new ArrayList<>();
/**
* 通过 AnalysisContext 对象可以获取到当前sheet,当前行等数据信息
*
* @param data 数据
* @param context 表格对象
*/
@Override
public void invoke(Object data, AnalysisContext context) {
//数据存储到list,供批量处理,或后续自己业务逻辑处理。
log.info("读取到数据{}", data);
dataList.add(data);
//根据业务自行处理,可以写入数据库等等
}
/**
* 数据解析完成回调
*
* @param context 表格对象
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.info("所有数据解析完成");
}
}
3、导出
/**
* 导出数据,尽量导出成低版本
*/
@GetMapping("/exportData")
public void exportData(HttpServletResponse response) throws Exception {
//生成工作簿
XSSFWorkbook workbook = new XSSFWorkbook();
//定义列标题,尽量遵从实体类的index
String[] columnNames = {"用户名", "年龄", "性别", "手机号"};
//定义工作表
Sheet sheet = workbook.createSheet();
//定义标题样式
Font titleFont = workbook.createFont();
titleFont.setFontName("宋体");
titleFont.setBold(true);
titleFont.setColor(IndexedColors.BLACK.index);
//设置单元格样式
XSSFCellStyle titleStyle = workbook.createCellStyle();
titleStyle.setAlignment(HorizontalAlignment.CENTER);
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
titleStyle.setFillForegroundColor(IndexedColors.YELLOW.index);
titleStyle.setFont(titleFont);
//定义行
Row titleRow = sheet.createRow(0);
//设置列
for (int i = 0; i < columnNames.length; i++) {
Cell cell = titleRow.createCell(i);
cell.setCellValue(columnNames[i]);
cell.setCellStyle(titleStyle);
}
//模拟构造数据
List<UserExcelModel> dataList = new ArrayList<>();
dataList.add(new UserExcelModel("张三", 12, "男", "13867098765"));
dataList.add(new UserExcelModel("张三1", 12, "男", "13867098765"));
dataList.add(new UserExcelModel("张三2", 12, "男", "13867098765"));
dataList.add(new UserExcelModel("张三3", 12, "男", "13867098765"));
//创建数据行并写入值
for (UserExcelModel userExcelModel : dataList) {
int lastRowNum = sheet.getLastRowNum();
Row dataRow = sheet.createRow(lastRowNum + 1);
// 此处注意index的顺序一定要严格按照模板文件的格式
dataRow.createCell(0).setCellValue(userExcelModel.getName());
dataRow.createCell(1).setCellValue(userExcelModel.getAge());
dataRow.createCell(2).setCellValue(userExcelModel.getSex());
dataRow.createCell(3).setCellValue(userExcelModel.getMobile());
}
response.setContentType("application/vnd.ms-excel");
response.setHeader("content-Disposition", "attachment;filename=" + URLEncoder.encode("easyexcel.xls", "utf-8"));
response.setHeader("Access-Control-Expose-Headers", "content-Disposition");
OutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
outputStream.flush();
outputStream.close();
}