说明:本工具适用大部分导入导出场景,花点时间搞懂怎么用了之后灰常方便,阅读本文前,建议下载源码后参考着阅读。源码地址:https://gitee.com/xwzhang1/excel-util.git
在使用过程中如果有不懂的地方,请随时在项目issue中提问,我会在最短的时间内答复。 如果有好的建议也十分希望不吝赐教。
目录
导语
第一步 创建项目
第二步 引入POI依赖
第三步 开始撸代码
讲道理,建议结合源码看,源码地址介绍的比这里简单多了,光看这篇文章。。。我自己都不想看!
用户上传一个表格文件的时候,大部分表格中主要包含两类信息,一类我称之为散数据,如:定义在表格开头的 有效开始时间 或者 作者 等,另一类我称之为核心数据,也就是该表格中的主要数据。
对于获取表格数据的想法是这样:
散数据 通过行列参数获取
核心数据 通过表格逐行遍历获取,并且用一个List
可能面临的问题:
1. 用户不按照规定的模板写文件,还上传!!!!!我擦!搞个屁,不按照模板还要我处理,处理毛线啊!!!
2. 不按照模板处理也分为两种,一种是数据出现的位置,也就是模板的第一行第二列应该写入作者名字,但是用户偏偏把名字写在了第100行第1000列??还有人性吗? WTF!! 反正我没法搞定。还有一种稍微人性化一点的问题,数据格式不正确,数据重复验证,多列数据组合重复验证,数据能否为空等,像这样人性化一点的问题,我也将尝试着干一下。
3. 如果用户上传的文件中存在明显或者不明显的错误数据,如何告诉用户?
最后再用几句话总结一下,这个工具到底想干嘛:
1 - 将文件流转换成POI可以处理的对象格式
2 - 处理的时候,把核心数据取出来放到预定义的对象中
3 - 在转换过程中对可能出现的人性化的错误进行收集
4 - 对于散数据,提供一个方法,参数为行列值即可返回相应值,至于怎么处理由开发者自己根据业务需求决定。
5 - 将收集到的每一行的错误信息重写到表格文件中,并将得到的文件进行存储(计划使用Redis),提供给用户下载
说这么多空话,我们来开始干吧。
哦对了,POI我不做介绍了吧,还不了解的百度一下就行啦。
开始了。
创建项目不用教程吧。项目结构?帖子最后会贴上源码地址。
我用的是IntelliJ Idea,新建项目
File -> New Project -> Spring Iniyializr ->Project SDK 1.8 -> ......
名字什么的自己起了,我创建的是一个SpringBoot Web项目,因为最后要通过接口从前端获取文件流。
创建完成之后再在启动类的同目录下新建几个文件夹,结构就是如图所示
pom.xml文件是这样
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.1.RELEASE
com.poi
excel-deal
0.0.1-SNAPSHOT
excel-deal
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.apache.poi
poi
3.9
org.apache.poi
poi-ooxml
3.9
org.projectlombok
lombok
javax.validation
validation-api
org.springframework.boot
spring-boot-maven-plugin
org.apache.poi
poi
3.9
org.apache.poi
poi-ooxml
3.9
后边由于内容比较多,细节也比较多,所以大多数需要注意的地方我会在代码中用备注的形式提示,其实逻辑都很简单,如果想了解细节,只需要跟着代码走一遍就清楚了。
3.1 创建类ExcelImportObject,用于定义在转换过程中需要预定义的各类参数。
package com.poi.exceldeal.util;
import lombok.Data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* 关于涉及到表格行列的属性,统一都是从1开始计数
*
*
* @author zhangxianwen
* @since 2018/12/20 15:39
*/
@Data
public class ExcelImportObject {
/**
* 待转换的文件流byte
*/
byte[] fileInputStream;
/**
* 转换成功结果List[该结果仅仅是对数据在进行表格到对象之间的转换时成功的条数]
*/
private List successList = new ArrayList<>();
/**
* 转换失败结果List[该结果仅仅是对数据在进行表格到对象之间的转换时失败的条数]
*/
private List failedList = new ArrayList<>();
/**
* 无论该行数据转换失败与否均写入到该List
*/
private List resultList = new ArrayList<>();
/**
* 最终校验成功可用于导入的List对象
*/
private List importList = new ArrayList<>();
/**
* 单元格是否允许有空值
* 默认不允许
*/
private Boolean notNullCell = true;
/**
* 从第几行开始读取[序号] 为空时, 若标注了标题行,则鼻癌提行下一行为开始读取的行,否则从首行
* 注意:在选取行的时候请选择主要数据开始的行,不包含标题以及其他备注信息
*/
private Integer firstRowNum;
/**
* 第几行结束读取[序号],选填
* 当该参数为空时,默认读取到整个表格没有数据的一行
*/
private Integer lastRowNum;
/**
* 首列[从1开始计数]
*/
private Integer firstCellNum;
/**
* 操作的列数
*/
private Integer cellCount;
/**
* 标题行[从0开始计数] 默认为操作开始行的前一行
*/
private Integer titleRowNum;
/**
* 验证成功条数[该结果仅仅是对数据在进行表格到对象之间的转换时成功的条数]
*/
private int successCount = 0;
/**
* 验证失败条数[该结果仅仅是对数据在进行表格到对象之间的转换时失败的条数]
*/
private int failedCount = 0;
/**
* 需要验证重复数据的列序号 如: new Integer[]{1,2,3}
*/
private Integer[] verifyDuplicationCells = {};
/**
* 多列组合后不得有重复,主要列放开头 如: new Integer[]{3,1,2} 表示1,2,3列组合起来不得有重复,其中第三列为重复提示信息,第1和2列仅仅是辅助第3列验证
*/
private Integer[] uniteVerifyDuplicationCells = {};
/**
* true 存在标题行
* false 不存在标题行
*/
private Boolean existTitle = true;
/**
* true 一条失败全部失败(默认)
* false 成功执行,失败返回
*/
private Boolean oneFailAllFail = true;
/**
* 数据验证错误信息 KEY为错误的行 VALUE为错误信息
*/
private Map errorMap = new HashMap<>();
}
(其中定义在类头的@Data注解用于自动Get、Set以及其他,依赖为:
org.projectlombok
lombok
3.2 创建ExcelUtil类,用于处理核心逻辑。
3.2.1 首先是将表格中的内容进行验证收集错误信息,并将数据写入目标对象,由于在实际业务中。我们并不知道表格中的数据将会对应到是样的对象,但是工具又需要做到通用,所以在接收目标对象的时候使用泛型接收,最后通过反射解析到该对象中的信息。
创建名称为exchangeExcelStreamToList的方法,入参为一个Class
其中:
Class
ExcelImportObject
package com.poi.exceldeal.util;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
*
*
*
*
* @author zhangxianwen
* @since 2018/12/20 15:39
*/
public class ExcelUtil {
private static final String LONG = "long";
private static final String M_LONG = "Long";
private static final String FLOAT = "Float";
private static final String M_FLOAT = "float";
private static final String DOUBLE = "Double";
private static final String M_DOUBLE = "double";
private static final String INTEGER = "Integer";
private static final String INT = "int";
private static final String BIGDECIMAL = "BigDecimal";
private static final String LOCALDATE = "LocalDate";
private static final String LOCALDATETIME = "LocalDateTime";
private static final String LOCALTIME = "LocalTime";
private static final String SET_NULL = "SET_NULL";
private static final String INVALID_DATA = "INVALID_DATA";
/**
* 传入类和ExcelTransferRule对象,其中包含文件流
* 处理中,当某行数据处理出现错误后,该行数据将以空值的形式赋值给目标对象[所以此处要求,开发者在创建对应实体对象的时候不要使用基本数据类型]
*
* @param clazz 目标类对象
* @param rule 表格转换规则对象
* @param 任意继承了AbstractExcelRequest的实体类
* @return
* @throws IllegalAccessException
* @throws InstantiationException
* @throws IOException
*/
public static ExcelImportObject exchangeExcelStreamToList(Class clazz, ExcelImportObject rule) throws IOException, IllegalAccessException, InstantiationException {
if (rule.getFileInputStream().length <= 0) {
// 文件输入流为空时的处理逻辑
}
// 1. 将文件流转换成可以处理的Workbook对象
InputStream inputStream = new ByteArrayInputStream(rule.getFileInputStream());
Workbook workbook = new XSSFWorkbook(inputStream);
// 2. 获取工作簿中的第一个工作表(一个表格中会有N个工作表) 本工具默认只处理第一个工作表
Sheet sheetAt = workbook.getSheetAt(0);
// 3. 获取Class对象的属性信息
Field[] fields = clazz.getDeclaredFields();
// 4. 规范在表格操作中将会使用到的操作首行,操作尾行,操作首列,操作尾列等参数
// 4.1 开始操作的行
int firstRow;
if (rule.getFirstRowNum() == null) {
if (rule.getTitleRowNum() != null) {
rule.setFirstRowNum(rule.getTitleRowNum() + 1);
} else {
rule.setFirstRowNum(1);
}
}
firstRow = rule.getFirstRowNum() - 1;
// 4.2 结束操作的行
int lastRow;
if (rule.getLastRowNum() == null) {
rule.setLastRowNum(sheetAt.getLastRowNum() + 1);
}
lastRow = rule.getLastRowNum() - 1;
// 4.3 开始操作的列
int firstCell;
if (rule.getFirstCellNum() == null) {
rule.setFirstCellNum(1);
}
firstCell = rule.getFirstCellNum() - 1;
// 4.4 操作的列数[当自定义的操作列数大于制定对象属性数时,强制将操作列数指定为对象属性个数,嗯~~~ 是有点不符合开发思想,但是管不了辣么多了]
int cellCount;
if (rule.getCellCount() == null || rule.getCellCount() > fields.length) {
rule.setCellCount(fields.length);
}
cellCount = rule.getCellCount();
// 4.5 标题行
Integer titleRow = null;
if (firstRow > 0) {
if (rule.getExistTitle()) {
if (rule.getTitleRowNum() == null) {
rule.setTitleRowNum(firstRow);
}
titleRow = rule.getTitleRowNum() - 1;
}
}
// 5. 定义相关输出对象
// 5.1 转换成功对象
List successList = new ArrayList<>();
// 5.2 转换失败对象
List failedList = new ArrayList<>();
// 5.3 成功与否都怼进去对象
List resultList = new ArrayList<>();
// 5.4 单列验重set
Set verifyDuplicationSet = new HashSet<>();
// 5.5 多列组合验重set
Set uniteVerifyDuplicationSet = new HashSet<>();
// 5.6 错误信息
Map errorMap = new HashMap<>();
// 6. 遍历表格数据
for (int i = firstRow; i <= lastRow; i++) {
// 初始化类对象
T tClass = clazz.newInstance();
// 定义验证成功与否标志
boolean isSuccess = true;
// 经过验证后得到的错误信息
StringBuffer buffer = new StringBuffer();
// 单行多列数据拼接,用于多行联合验重
StringBuilder uniteCellsBuffer = new StringBuilder();
for (int j = firstCell; j <= firstCell + cellCount - 1; j++) {
if (sheetAt.getRow(i) == null) {
if (!rule.getNotNullCell()) {
// 当配置表格数据可以为空
sheetAt.createRow(i).createCell(j).setCellValue(ExcelUtil.SET_NULL);
} else {
// 当配置表格数据不可以为空
buffer.append("行不能为空,");
isSuccess = false;
break;
}
}
if (sheetAt.getRow(i).getCell(j) == null) {
if (!rule.getNotNullCell()) {
// 当配置表格数据可以为空
sheetAt.getRow(i).createCell(j).setCellValue(ExcelUtil.SET_NULL);
} else {
if (titleRow != null && sheetAt.getRow(titleRow) != null && sheetAt.getRow(titleRow).getCell(j) != null) {
// 当标题存在时,错误信息按照标题提示
buffer.append(sheetAt.getRow(titleRow).getCell(j).getStringCellValue()).append("不能为空!");
} else {
buffer.append("第").append(j + 1).append("列不能有空值,");
}
isSuccess = false;
break;
}
}
// 允许对对象中的属性进行操作
fields[j].setAccessible(true);
// 将每一个单元格中的内容都视为String类型[统一处理,否则传过来的类型会被自动识别成各种乱七八糟的类型,不利于之后的转换]
sheetAt.getRow(i).getCell(j).setCellType(Cell.CELL_TYPE_STRING);
// 单列验重
if (Arrays.asList(rule.getVerifyDuplicationCells()).contains(j + 1)) {
if (!ExcelUtil.SET_NULL.equals(sheetAt.getRow(i).getCell(j).getStringCellValue().trim())) {
// 非空数据验重[为啥 空数据不验证呢 = = 暂时没想好为啥]
if (!verifyDuplicationSet.add(j + "_" + sheetAt.getRow(i).getCell(j).getStringCellValue().trim())) {
if (titleRow != null && sheetAt.getRow(titleRow) != null && sheetAt.getRow(titleRow).getCell(j) != null) {
buffer.append(sheetAt.getRow(titleRow).getCell(j).getStringCellValue()).append("存在重复");
} else {
buffer.append("第").append(j + 1).append("列数据存在重复,");
}
isSuccess = false;
}
}
}
// 多列联合验重(思想是,把这几个数据拼接成字符串,最后利用set即可达到验证制定的多列数据的效果)
if (Arrays.asList(rule.getUniteVerifyDuplicationCells()).contains(j + 1)) {
uniteCellsBuffer.append(sheetAt.getRow(i).getCell(j).getStringCellValue().trim());
}
// 获取对象中指定属性的属性类型
Class type = fields[j].getType();
// 根据属性类型对表格数据进行转换,转换出现异常时记录错误信息
Object o = typeTransfer(type, sheetAt.getRow(i).getCell(j).getStringCellValue());
if (ExcelUtil.INVALID_DATA.equals(o)) {
if (titleRow != null && sheetAt.getRow(titleRow) != null && sheetAt.getRow(titleRow).getCell(j) != null) {
buffer.append(sheetAt.getRow(titleRow).getCell(j).getStringCellValue()).append("数据类型无效,已当做空值处理");
} else {
buffer.append("第").append(j + 1).append("列数据类型无效,已当做空值处理,请检查,");
}
isSuccess = false;
break;
}
// 将值赋给对象中对应属性
fields[j].set(tClass, o);
}
// 处理多列联合验重
if (uniteCellsBuffer.length() > 0 && !uniteVerifyDuplicationSet.add(uniteCellsBuffer.toString())) {
if (titleRow != null && sheetAt.getRow(titleRow) != null && sheetAt.getRow(titleRow).getCell(rule.getUniteVerifyDuplicationCells()[0] - 1) != null) {
buffer.append(sheetAt.getRow(titleRow).getCell(rule.getUniteVerifyDuplicationCells()[0] - 1).getStringCellValue()).append("数据重复");
} else {
buffer.append("无效数据,第").append(Arrays.toString(rule.getUniteVerifyDuplicationCells())).append("列中数据联和存在重复");
}
}
if (buffer.length() > 0) {
errorMap.put(i, buffer);
}
rule.getErrorMap().putAll(errorMap);
if (rule.getOneFailAllFail()) {
// 当一列出错所有数据返回时
failedList.add(tClass);
} else {
if (isSuccess) {
successList.add(tClass);
} else {
failedList.add(tClass);
}
}
resultList.add(tClass);
}
// 7. 处理结果数据
rule.setFailedList(failedList);
rule.setSuccessList(successList);
rule.setResultList(resultList);
rule.setFailedCount(rule.getFailedList().size());
rule.setSuccessCount(rule.getSuccessList().size());
return rule;
}
/**
* 将字符串str转为clazz对应的类型
*
* @param clazz 位置类
* @param str 源字符串
* @return 返回转换后得到的对象
*/
private static Object typeTransfer(Class clazz, String str) {
String clazzStr = clazz.toString();
if (str.equals(ExcelUtil.SET_NULL)) {
return null;
}
try {
if (clazzStr.endsWith(ExcelUtil.INTEGER) || clazzStr.endsWith(ExcelUtil.INT)) {
return Integer.valueOf(str.trim());
}
if (clazzStr.endsWith(ExcelUtil.M_LONG) || clazzStr.endsWith(ExcelUtil.LONG)) {
return Long.valueOf(str.trim());
}
if (clazzStr.endsWith(ExcelUtil.DOUBLE) || clazzStr.endsWith(ExcelUtil.M_DOUBLE)) {
return Double.valueOf(str.trim());
}
if (clazzStr.endsWith(ExcelUtil.FLOAT) || clazzStr.endsWith(ExcelUtil.M_FLOAT)) {
return Float.valueOf(str.trim());
}
if (clazzStr.endsWith(ExcelUtil.BIGDECIMAL)) {
return new BigDecimal(str.trim());
}
if (clazzStr.endsWith(ExcelUtil.LOCALDATE)) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
return LocalDate.parse(str, fmt);
}
if (clazzStr.endsWith(ExcelUtil.LOCALDATETIME)) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDate.parse(str, fmt);
}
if (clazzStr.endsWith(ExcelUtil.LOCALTIME)) {
return LocalTime.parse(str, DateTimeFormatter.ISO_TIME);
} else {
return str;
}
} catch (Exception e) {
return ExcelUtil.INVALID_DATA;
}
}
/**
* 将写入错误信息的文件流建议存到redis提供下载,本demo中不做演示
*
* @param rule
* @return
* @throws IOException
*/
@SuppressWarnings("unchecked")
public static byte[] writeErrorMessageToExcel(ExcelImportObject rule) throws IOException {
// 1. 获取到ExcelRule中错误信息[该错误信息在数据验证中写入]
Map errorMap = new HashMap<>(rule.getErrorMap());
// 2. 类似于第一个方法,重新获取到文件流,相当于重新初始化用户上传的文件
ByteArrayOutputStream os = new ByteArrayOutputStream();
Integer lastCell = rule.getFirstCellNum() + rule.getCellCount() - 1;
InputStream inputStream = new ByteArrayInputStream(rule.getFileInputStream());
Workbook workbook = new XSSFWorkbook(inputStream);
// 3. 定义了一个加粗字体的Style,应用在标题上
CellStyle titleCellStyle = workbook.createCellStyle();
Font titleFont = workbook.createFont();
titleFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
titleCellStyle.setFont(titleFont);
// 4. 定义使字体变成红色的Style,把错误的信息用红色字体展示
CellStyle redFontCellStyle = workbook.createCellStyle();
Font redFont = workbook.createFont();
redFont.setColor(Font.COLOR_RED);
redFontCellStyle.setFont(redFont);
Sheet sheetAt = workbook.getSheetAt(0);
// 当标题行存在时,在最后一列的后一列添加标题:失败原因
Integer titleRow = rule.getTitleRowNum() == null ? null : rule.getTitleRowNum() - 1;
if (titleRow != null) {
if (sheetAt.getRow(titleRow) == null) {
sheetAt.createRow(titleRow);
}
sheetAt.getRow(titleRow).createCell(lastCell).setCellValue("失败原因");
for (int i = 0; i <= lastCell; i++) {
if (sheetAt.getRow(titleRow).getCell(i) == null) {
sheetAt.getRow(titleRow).createCell(i);
}
sheetAt.getRow(titleRow).getCell(i).setCellStyle(titleCellStyle);
}
}
// 根据传入的错误信息进行核心内容错误数据写入
for (Map.Entry entry : errorMap.entrySet()) {
Integer rowNum = entry.getKey();
if (sheetAt.getRow(rowNum) == null) {
sheetAt.createRow(rowNum).createCell(lastCell).setCellValue(entry.getValue().toString());
} else {
sheetAt.getRow(rowNum).createCell(lastCell).setCellValue(entry.getValue().toString());
}
sheetAt.getRow(rowNum).getCell(lastCell).setCellStyle(redFontCellStyle);
}
// 非一错全错时
if (!rule.getOneFailAllFail()) {
int i = rule.getFirstRowNum() - 1;
for (; i < sheetAt.getLastRowNum(); i++) {
if (sheetAt.getRow(sheetAt.getLastRowNum()).getCell(lastCell) == null) {
sheetAt.removeRow(sheetAt.getRow(sheetAt.getLastRowNum()));
} else {
break;
}
}
i = rule.getFirstRowNum() - 1;
for (; i < sheetAt.getLastRowNum(); i++) {
int j = i + 1;
if (sheetAt.getRow(i).getCell(lastCell) == null) {
sheetAt.shiftRows(j, sheetAt.getLastRowNum(), -1);
i = i - 1;
}
}
}
// 得到添加了错误信息的表格文件,随后将其转换成文件流
workbook.write(os);
inputStream.close();
os.close();
return os.toByteArray();
}
/**
* 在对转换得到的对象进行逻辑校验时,使用List遍历校验,传入相应参数自动处理错误信息
*
* @param rule 规则参数,填写转换excelStreamToList方法的输出对象
* @param i 循环的index [0开始]
* @param errorMessage [错误信息]
* @param 泛型
*/
public static void errorMessageDeal(ExcelImportObject rule, int i, StringBuffer errorMessage) {
if (errorMessage.length() > 0) {
if (rule.getErrorMap().get(i + 1) == null) {
rule.getErrorMap().put(i + 1, errorMessage);
} else {
rule.getErrorMap().put(i + 1, errorMessage.append(rule.getErrorMap().get(i + 1)));
}
} else if (rule.getErrorMap().get(i + 1) == null && !rule.getOneFailAllFail()) {
rule.getImportList().add(rule.getResultList().get(i));
}
}
}
其中:
@SuppressWarnings("unchecked") 用于告诉idea不要检查HashMap的转换(不加也没关系)
到现在,一个基础的表格解析和错误信息的写入就实现。 关于将逻辑得到的结果也写到文件里边,我们稍后继续干!现在先测试一下这个基础的功能。
① 在facade包中创建预置实体类ExcelModel,添加两个属性:
package com.poi.exceldeal.facade;
import com.poi.exceldeal.util.ExcelModelRequest;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
*
*
*
*
* @author zhangxianwen
* @since 2018/11/28 10:26
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ExcelModel extends ExcelModelRequest {
@NotBlank(message = "姓名不能为空,")
@Size(min = 3, max = 5, message = "姓名个数必须在3到5个字之间,")
private String name;
@NotNull(message = "年龄不能为空,")
@Max(value = 100, message = "年龄不能超过100,")
private Integer age;
}
② 在之前创建的controller中创建类DealExcelController,并实现以下方法
package com.poi.exceldeal.controller;
import com.poi.exceldeal.util.HttpConfig;
import com.poi.exceldeal.webservice.ExcelWebService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
* 可以理解为入口和出口
*
*
* @author zhangxianwen
* @since 2018/12/20 15:37
*/
@RestController
@RequestMapping("/excel")
@Slf4j
public class DealExcelController {
public static byte[] outPutByte = new byte[0];
@Autowired
private ExcelWebService service;
@PostMapping(value = "/set")
public String setErrorFile(HttpServletRequest request, HttpServletResponse response) throws IOException, InstantiationException, IllegalAccessException, NoSuchMethodException, NoSuchFieldException {
MultipartFile uploadFile = ((MultipartHttpServletRequest) request).getFile("uploadFile");
outPutByte = service.importShopCostPriceScope(uploadFile.getBytes());
// 异常逻辑自行处理
return "处理成功";
}
@GetMapping(value = "/get")
public String getErrorFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
HttpConfig.setExcelResponseModel("奖励规则适用范围导入错误数据", outPutByte, response);
// 各种可能出现异常的请自行判断
return "导出成功";
}
}
③ 其中HttpConfig.setExcelResponseModel()犯法用于定义响应参数,通过HttpServletResponse将我们提供的表格文件提供给前端下载。
package com.poi.exceldeal.util;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
/**
*
*
*
*
* @author zhangxianwen
* @since 2018/12/18 17:31
*/
public class HttpConfig {
public static void setExcelResponseModel(byte[] outPutByte, HttpServletResponse response) throws IOException {
HttpConfig.setExcelResponseModel("outPutFile", outPutByte, response);
}
public static void setExcelResponseModel(String fileName, byte[] outPutByte, HttpServletResponse response) throws IOException {
response.reset();
response.setContentType("application/msexcel;charset=utf-8");
response.setHeader("Content-disposition", "attachment;filename= " + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
response.addHeader("Cache-Control", "no-cache");
response.getOutputStream().write(outPutByte);
response.getOutputStream().flush();
response.getOutputStream().close();
}
}
好了,现在和前端的桥梁搭好了,参数也配置好了,启动项目=================
哦对了,到目前为止,我们的项目结构是这样的
。项目启动后,默认端口是8080。
创建一个表格文件,内容如下:
启动postman [用其他的也可以,总归要把请求发出去] ,输入地址:
http://localhost:8080/excel/set
请求发送后,再通过获取文件接口获取到写了失败信息的文件,打开浏览器,输入地址
http://localhost:8080/excel/get
会自动下载文件,就是图中这样 ↓
哇,心痛不行了,不想写了。还有导出的功能,移步项目地址吧!
https://gitee.com/xwzhang1/excel-util.git