Java-Excel导入导出通用实现Demo(附源码Git地址)

说明:本工具适用大部分导入导出场景,花点时间搞懂怎么用了之后灰常方便,阅读本文前,建议下载源码后参考着阅读。源码地址:https://gitee.com/xwzhang1/excel-util.git

在使用过程中如果有不懂的地方,请随时在项目issue中提问,我会在最短的时间内答复。  如果有好的建议也十分希望不吝赐教。


目录

导语

第一步 创建项目

第二步 引入POI依赖

第三步 开始撸代码


讲道理,建议结合源码看,源码地址介绍的比这里简单多了,光看这篇文章。。。我自己都不想看!

Java-Excel导入导出通用实现Demo(附源码Git地址)_第1张图片

导语

用户上传一个表格文件的时候,大部分表格中主要包含两类信息,一类我称之为散数据,如:定义在表格开头的 有效开始时间 或者 作者 等,另一类我称之为核心数据,也就是该表格中的主要数据。

对于获取表格数据的想法是这样:

散数据 通过行列参数获取

核心数据 通过表格逐行遍历获取,并且用一个List 装起来。


可能面临的问题:

1. 用户不按照规定的模板写文件,还上传!!!!!我擦!搞个屁,不按照模板还要我处理,处理毛线啊!!!

Java-Excel导入导出通用实现Demo(附源码Git地址)_第2张图片

2. 不按照模板处理也分为两种,一种是数据出现的位置,也就是模板的第一行第二列应该写入作者名字,但是用户偏偏把名字写在了第100行第1000列??还有人性吗? WTF!! 反正我没法搞定。还有一种稍微人性化一点的问题,数据格式不正确,数据重复验证,多列数据组合重复验证,数据能否为空等,像这样人性化一点的问题,我也将尝试着干一下。

Java-Excel导入导出通用实现Demo(附源码Git地址)_第3张图片

3. 如果用户上传的文件中存在明显或者不明显的错误数据,如何告诉用户?


最后再用几句话总结一下,这个工具到底想干嘛:

1 - 将文件流转换成POI可以处理的对象格式

2 - 处理的时候,把核心数据取出来放到预定义的对象中

3 - 在转换过程中对可能出现的人性化的错误进行收集

4 - 对于散数据,提供一个方法,参数为行列值即可返回相应值,至于怎么处理由开发者自己根据业务需求决定。

5 - 将收集到的每一行的错误信息重写到表格文件中,并将得到的文件进行存储(计划使用Redis),提供给用户下载


说这么多空话,我们来开始干吧。

Java-Excel导入导出通用实现Demo(附源码Git地址)_第4张图片

哦对了,POI我不做介绍了吧,还不了解的百度一下就行啦。

开始了。


第一步 创建项目

创建项目不用教程吧。项目结构?帖子最后会贴上源码地址。

我用的是IntelliJ Idea,新建项目

File -> New Project -> Spring Iniyializr ->Project SDK 1.8 -> ......

名字什么的自己起了,我创建的是一个SpringBoot Web项目,因为最后要通过接口从前端获取文件流。

创建完成之后再在启动类的同目录下新建几个文件夹,结构就是如图所示

Java-Excel导入导出通用实现Demo(附源码Git地址)_第5张图片

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
            
        
    


 

第二步 引入POI依赖(第一步中是所有的依赖)

中添加poi的依赖用于支持表格文件的读取和操作

        
            org.apache.poi
            poi
            3.9
        
        
            org.apache.poi
            poi-ooxml
            3.9
        

 

第三步 开始撸代码

Java-Excel导入导出通用实现Demo(附源码Git地址)_第6张图片

后边由于内容比较多,细节也比较多,所以大多数需要注意的地方我会在代码中用备注的形式提示,其实逻辑都很简单,如果想了解细节,只需要跟着代码走一遍就清楚了。

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对象和3.1创建的ExcelImportObject对象,出参同样为该规则类,在该工具中,我将所有的入参出参以及各类指标都统一在一个类也就是ExcelImportObject中。

其中:

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(); } }

 

好了,现在和前端的桥梁搭好了,参数也配置好了,启动项目=================

哦对了,到目前为止,我们的项目结构是这样的

Java-Excel导入导出通用实现Demo(附源码Git地址)_第7张图片

。项目启动后,默认端口是8080。

创建一个表格文件,内容如下:

Java-Excel导入导出通用实现Demo(附源码Git地址)_第8张图片

 

启动postman [用其他的也可以,总归要把请求发出去] ,输入地址:

http://localhost:8080/excel/set

Java-Excel导入导出通用实现Demo(附源码Git地址)_第9张图片

 

请求发送后,再通过获取文件接口获取到写了失败信息的文件,打开浏览器,输入地址

http://localhost:8080/excel/get

会自动下载文件,就是图中这样 ↓

Java-Excel导入导出通用实现Demo(附源码Git地址)_第10张图片

 

哇,心痛不行了,不想写了。还有导出的功能,移步项目地址吧!

https://gitee.com/xwzhang1/excel-util.git

 

你可能感兴趣的:(excel)