【Java基础】Java导Excel攻略

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
img

  • 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老
  • 导航
    • 檀越剑指大厂系列:全面总结 java 核心技术点,如集合,jvm,并发编程 redis,kafka,Spring,微服务,Netty 等
    • 常用开发工具系列:罗列常用的开发工具,如 IDEA,Mac,Alfred,electerm,Git,typora,apifox 等
    • 数据库系列:详细总结了常用数据库 mysql 技术点,以及工作中遇到的 mysql 问题等
    • 懒人运维系列:总结好用的命令,解放双手不香吗?能用一个命令完成绝不用两个操作
    • 数据结构与算法系列:总结数据结构和算法,不同类型针对性训练,提升编程思维,剑指大厂

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨

博客目录

    • 一.简单介绍
      • 1.需求背景
      • 2.常见情形
    • 二.基础使用
      • 1.Apache POI
      • 2.pom 依赖
      • 3.具体实现
    • 三.EasyExcel
      • 1. EasyExcel
      • 2.相关特性
      • 3.简单示例
    • 四.进阶使用
      • 1. EasyExcelUtil
      • 2.EasyExcelWriterFactory
      • 3.ExcelUtil
      • 4.使用方式
      • 5.如何处理不确定的字段?
      • 6.需要注意的点

一.简单介绍

1.需求背景

在项目开发过程中,很多时候需要用到导出功能,将表格中的数据通过导出到 excel 的方式给到业务方核对数据,在模版不固定的情况下,如何快速的导出数据到 excel 显得尤为关键,本文将介绍导出 excel 的方式。

2.常见情形

  1. 报表生成: 在业务应用中,经常需要生成各种形式的报表,以便对数据进行汇总、分析和可视化展示。将数据导出到 Excel 文件可以让用户方便地使用 Excel 等工具进行进一步的数据处理和分析。
  2. 数据备份: 导出数据到 Excel 文件是一种常见的备份手段。用户可以定期将系统中的关键数据导出到 Excel,以便在需要时进行恢复或迁移。
  3. 数据交换: 在与其他系统或应用程序进行数据交换时,导出数据到 Excel 是一种通用的方式。Excel 文件格式是广泛支持的,易于在不同系统之间进行数据传递。
  4. 用户下载: 提供给用户下载其个人或业务数据的功能。这对于在线服务、电子商务平台等应用程序是很常见的需求,用户可以将其数据保存到本地以备查阅。
  5. 批量操作: 在某些情况下,用户可能需要对大量数据进行批量操作,例如批量更新、删除或进行其他处理。将数据导出到 Excel,用户可以在本地应用程序中更轻松地执行这些操作。
  6. 报价单、发票等业务文档: 在销售和财务领域,导出 Excel 可以用于生成报价单、发票和其他业务文档,这些文档通常需要以表格形式呈现。
  7. 数据分享: 有时,用户可能希望分享特定数据的快照或分析结果。将数据导出到 Excel 文件可以方便地与其他人共享数据。

二.基础使用

1.Apache POI

在 Java 中使用 POI 库(Apache POI)可以方便地操作 Excel 文件,包括导出数据和设置单元格的样式,其中包括背景颜色。下面是一个简单的例子,演示如何在 Java 中使用 POI 库导出带有背景颜色的 Excel 文件。

2.pom 依赖

首先,确保你的项目中包含了 Apache POI 库的依赖。如果使用 Maven,可以在 pom.xml 文件中添加以下依赖:

<dependencies><!-- Apache POI --><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.0.0</version></dependency>
</dependencies>

3.具体实现

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileOutputStream;
import java.io.IOException;public class ExcelExporter {public static void main(String[] args) {try {// 创建工作簿Workbook workbook = new XSSFWorkbook();// 创建工作表Sheet sheet = workbook.createSheet("Sheet1");// 创建样式CellStyle style = workbook.createCellStyle();style.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());style.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 创建行和单元格,并设置背景颜色Row row = sheet.createRow(0);Cell cell = row.createCell(0);cell.setCellValue("内容");cell.setCellStyle(style);// 导出文件try (FileOutputStream fileOut = new FileOutputStream("workbook.xlsx")) {workbook.write(fileOut);}// 关闭工作簿workbook.close();} catch (IOException e) {e.printStackTrace();}}
}

三.EasyExcel

1. EasyExcel

上述使用的原生的 Apache POI 通用性不够强,在某些方面使用起来还是不够方便,接下来将介绍阿里的 EasyExcel 的使用,在 POI 的基础上进行了封装,方便开发者直接使用。

EasyExcel 是阿里巴巴开源的一款基于 Java 的简单、高效的 Excel 文件读写工具。它可以帮助开发者更方便地进行 Excel 文件的读写操作,支持读取大数据量的 Excel 文件并且性能较好。

2.相关特性

  1. 简单易用: EasyExcel 提供了简单的 API,易于上手,使用起来相对轻松。
  2. 高性能: EasyExcel 使用了基于注解的对象模型,采用零反射、零异常的设计,因此性能较好。在处理大量数据时,相比于一些其他库,EasyExcel 通常表现更出色。
  3. 支持读写 Excel: 提供了读写 Excel 文件的功能,可以实现从 Excel 文件中读取数据,也可以将数据写入 Excel 文件。
  4. 支持多种数据模型: 可以支持 Java 普通对象、Map、List 等多种数据模型,便于适应不同的数据结构。
  5. 支持复杂报表: 可以实现复杂报表的导入导出,包括多表头、合并单元格等。
  6. 支持自定义样式: 可以自定义 Excel 单元格样式,包括字体、颜色、边框等。
  7. 支持多种 Excel 格式: 可以读写多种 Excel 格式,包括 xls 和 xlsx。

3.简单示例

读取 Excel:

// 读取 Excel 文件
String fileName = "example.xlsx";
EasyExcel.read(fileName, UserData.class, new UserDataListener()).sheet().doRead();

写入 Excel:

// 写入 Excel 文件
String fileName = "example.xlsx";
List<UserData> data = initData(); // 初始化数据
EasyExcel.write(fileName, UserData.class).sheet("Sheet1").doWrite(data);

监听器示例:

public class UserDataListener extends AnalysisEventListener<UserData> {// 处理每一行的数据@Overridepublic void invoke(UserData data, AnalysisContext context) {System.out.println("Read data: " + data);}// 所有数据解析完成后调用@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {System.out.println("All data parsed successfully.");}
}

这只是一个简单的示例,实际使用中可以根据需求进行更灵活和复杂的配置。EasyExcel 提供了更多的 API 和功能,以满足不同场景下的需求。可以通过 EasyExcel 的官方文档和示例代码深入了解其更多功能和用法:EasyExcel GitHub 仓库。

四.进阶使用

1. EasyExcelUtil

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.handler.WriteHandler;
import org.apache.poi.ss.formula.functions.T;import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Set;public class EasyExcelUtil {/*** 同步无模型读(默认读取sheet0,从第2行开始读)** @param filePath excel文件的绝对路径*/public static List<Map<Integer, String>> syncRead(String filePath) {return EasyExcelFactory.read(filePath).sheet().doReadSync();}/*** 同步无模型读(自定义读取sheetX,从第2行开始读)** @param filePath excel文件的绝对路径* @param sheetNo  sheet页号,从0开始*/public static List<Map<Integer, String>> syncRead(String filePath, Integer sheetNo) {return EasyExcelFactory.read(filePath).sheet(sheetNo).doReadSync();}/*** 同步无模型读(指定sheet和表头占的行数)** @param filePath* @param sheetNo    sheet页号,从0开始* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static List<Map<Integer, String>> syncRead(String filePath, Integer sheetNo, Integer headRowNum) {return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();}/*** 同步无模型读(指定sheet和表头占的行数)** @param inputStream* @param sheetNo     sheet页号,从0开始* @param headRowNum  表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static List<Map<Integer, String>> syncRead(InputStream inputStream, Integer sheetNo, Integer headRowNum) {return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();}/*** 同步无模型读(指定sheet和表头占的行数)** @param file* @param sheetNo    sheet页号,从0开始* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static List<Map<Integer, String>> syncRead(File file, Integer sheetNo, Integer headRowNum) {return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();}
//====================================================无JAVA模型读取excel数据===============================================================//====================================================将excel数据同步到JAVA模型属性里===============================================================/*** 同步按模型读(默认读取sheet0,从第2行开始读)** @param filePath* @param clazz    模型的类类型(excel数据会按该类型转换成对象)*/public static List<T> syncReadModel(String filePath, Class clazz) {return EasyExcelFactory.read(filePath).sheet().head(clazz).doReadSync();}/*** 同步按模型读(默认表头占一行,从第2行开始读)** @param filePath* @param clazz    模型的类类型(excel数据会按该类型转换成对象)* @param sheetNo  sheet页号,从0开始*/public static List<T> syncReadModel(String filePath, Class clazz, Integer sheetNo) {return EasyExcelFactory.read(filePath).sheet(sheetNo).head(clazz).doReadSync();}/*** 同步按模型读(指定sheet和表头占的行数)** @param inputStream* @param clazz       模型的类类型(excel数据会按该类型转换成对象)* @param sheetNo     sheet页号,从0开始* @param headRowNum  表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static List<T> syncReadModel(InputStream inputStream, Class clazz, Integer sheetNo, Integer headRowNum) {return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();}/*** 同步按模型读(指定sheet和表头占的行数)** @param file* @param clazz      模型的类类型(excel数据会按该类型转换成对象)* @param sheetNo    sheet页号,从0开始* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static List<T> syncReadModel(File file, Class clazz, Integer sheetNo, Integer headRowNum) {return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();}/*** 同步按模型读(指定sheet和表头占的行数)** @param filePath* @param clazz      模型的类类型(excel数据会按该类型转换成对象)* @param sheetNo    sheet页号,从0开始* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static List<T> syncReadModel(String filePath, Class clazz, Integer sheetNo, Integer headRowNum) {return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();}/*** 异步无模型读(默认读取sheet0,从第2行开始读)** @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param filePath      表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener) {EasyExcelFactory.read(filePath, excelListener).sheet().doRead();}/*** 异步无模型读(默认表头占一行,从第2行开始读)** @param filePath      表头占的行数,从0开始(如果要连表头一起读出来则传0)* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param sheetNo       sheet页号,从0开始*/public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener, Integer sheetNo) {EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).doRead();}/*** 异步无模型读(指定sheet和表头占的行数)** @param inputStream* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param sheetNo       sheet页号,从0开始* @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static void asyncRead(InputStream inputStream, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {EasyExcelFactory.read(inputStream, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();}/*** 异步无模型读(指定sheet和表头占的行数)** @param file* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param sheetNo       sheet页号,从0开始* @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static void asyncRead(File file, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {EasyExcelFactory.read(file, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();}/*** 异步无模型读(指定sheet和表头占的行数)** @param filePath* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param sheetNo       sheet页号,从0开始* @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)* @return*/public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();}/*** 异步按模型读取(默认读取sheet0,从第2行开始读)** @param filePath* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param clazz         模型的类类型(excel数据会按该类型转换成对象)*/public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz) {EasyExcelFactory.read(filePath, clazz, excelListener).sheet().doRead();}/*** 异步按模型读取(默认表头占一行,从第2行开始读)** @param filePath* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param clazz         模型的类类型(excel数据会按该类型转换成对象)* @param sheetNo       sheet页号,从0开始*/public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo) {EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).doRead();}/*** 异步按模型读取** @param inputStream* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param clazz         模型的类类型(excel数据会按该类型转换成对象)* @param sheetNo       sheet页号,从0开始* @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static void asyncReadModel(InputStream inputStream, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {EasyExcelFactory.read(inputStream, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();}/*** 异步按模型读取** @param file* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param clazz         模型的类类型(excel数据会按该类型转换成对象)* @param sheetNo       sheet页号,从0开始* @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static void asyncReadModel(File file, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {EasyExcelFactory.read(file, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();}/*** 异步按模型读取** @param filePath* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等* @param clazz         模型的类类型(excel数据会按该类型转换成对象)* @param sheetNo       sheet页号,从0开始* @param headRowNum    表头占的行数,从0开始(如果要连表头一起读出来则传0)*/public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();}/*** 无模板写文件** @param filePath* @param head     表头数据* @param data     表内容数据*/public static void write(String filePath, List<List<String>> head, List<List<Object>> data) {EasyExcel.write(filePath).head(head).sheet().doWrite(data);}/*** 无模板写文件** @param filePath* @param head      表头数据* @param data      表内容数据* @param sheetNo   sheet页号,从0开始* @param sheetName sheet名称*/public static void write(String filePath, List<List<String>> head, List<List<Object>> data, Integer sheetNo, String sheetName) {EasyExcel.write(filePath).head(head).sheet(sheetNo, sheetName).doWrite(data);}/*** 根据excel模板文件写入文件** @param filePath* @param templateFileName* @param headClazz* @param data*/public static void writeTemplate(String filePath, String templateFileName, Class headClazz, List data) {EasyExcel.write(filePath, headClazz).withTemplate(templateFileName).sheet().doWrite(data);}/*** 根据excel模板文件写入文件** @param filePath* @param templateFileName* @param data*/public static void writeTemplate(String filePath, String templateFileName, List data) {EasyExcel.write(filePath).withTemplate(templateFileName).sheet().doWrite(data);}/*** 按模板写文件** @param filePath* @param headClazz 表头模板* @param data      数据*/public static void write(String filePath, Class headClazz, List data) {EasyExcel.write(filePath, headClazz).sheet().doWrite(data);}/*** 按模板写文件** @param filePath* @param headClazz 表头模板* @param data      数据* @param sheetNo   sheet页号,从0开始* @param sheetName sheet名称*/public static void write(String filePath, Class headClazz, List data, Integer sheetNo, String sheetName) {EasyExcel.write(filePath, headClazz).sheet(sheetNo, sheetName).doWrite(data);}/*** 按模板写文件** @param filePath* @param headClazz    表头模板* @param data         数据* @param writeHandler 自定义的处理器,比如设置table样式,设置超链接、单元格下拉框等等功能都可以通过这个实现(需要注册多个则自己通过链式去调用)* @param sheetNo      sheet页号,从0开始* @param sheetName    sheet名称*/public static void write(String filePath, Class headClazz, List data, WriteHandler writeHandler, Integer sheetNo, String sheetName) {EasyExcel.write(filePath, headClazz).registerWriteHandler(writeHandler).sheet(sheetNo, sheetName).doWrite(data);}/*** 按模板写文件(包含某些字段)** @param filePath* @param headClazz   表头模板* @param data        数据* @param includeCols 包含字段集合,根据字段名称显示* @param sheetNo     sheet页号,从0开始* @param sheetName   sheet名称*/public static void writeInclude(String filePath, Class headClazz, List data, Set<String> includeCols, Integer sheetNo, String sheetName) {EasyExcel.write(filePath, headClazz).includeColumnFiledNames(includeCols).sheet(sheetNo, sheetName).doWrite(data);}/*** 按模板写文件(排除某些字段)** @param filePath* @param headClazz   表头模板* @param data        数据* @param excludeCols 过滤排除的字段,根据字段名称过滤* @param sheetNo     sheet页号,从0开始* @param sheetName   sheet名称*/public static void writeExclude(String filePath, Class headClazz, List data, Set<String> excludeCols, Integer sheetNo, String sheetName) {EasyExcel.write(filePath, headClazz).excludeColumnFiledNames(excludeCols).sheet(sheetNo, sheetName).doWrite(data);}/*** 多个sheet页的数据链式写入* ExcelUtil.writeWithSheets(outputStream)* .writeModel(ExcelModel.class, excelModelList, "sheetName1")* .write(headData, data,"sheetName2")* .finish();** @param outputStream*/public static EasyExcelWriterFactory writeWithSheets(OutputStream outputStream) {EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(outputStream);return excelWriter;}/*** 多个sheet页的数据链式写入* ExcelUtil.writeWithSheets(file)* .writeModel(ExcelModel.class, excelModelList, "sheetName1")* .write(headData, data,"sheetName2")* .finish();** @param file*/public static EasyExcelWriterFactory writeWithSheets(File file) {EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(file);return excelWriter;}/*** 多个sheet页的数据链式写入* ExcelUtil.writeWithSheets(filePath)* .writeModel(ExcelModel.class, excelModelList, "sheetName1")* .write(headData, data,"sheetName2")* .finish();** @param filePath*/public static EasyExcelWriterFactory writeWithSheets(String filePath) {EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(filePath);return excelWriter;}/*** 多个sheet页的数据链式写入(失败了会返回一个有部分数据的Excel)* ExcelUtil.writeWithSheets(response, exportFileName)* .writeModel(ExcelModel.class, excelModelList, "sheetName1")* .write(headData, data,"sheetName2")* .finish();** @param response* @param exportFileName 导出的文件名称*/public static EasyExcelWriterFactory writeWithSheetsWeb(HttpServletResponse response, String exportFileName) throws IOException {response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码String fileName = URLEncoder.encode(exportFileName, "UTF-8");response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(response.getOutputStream());return excelWriter;}
}

2.EasyExcelWriterFactory

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;import java.io.File;
import java.io.OutputStream;
import java.util.List;public class EasyExcelWriterFactory {private int sheetNo = 0;private ExcelWriter excelWriter = null;public EasyExcelWriterFactory(OutputStream outputStream) {excelWriter = EasyExcel.write(outputStream).build();}public EasyExcelWriterFactory(File file) {excelWriter = EasyExcel.write(file).build();}public EasyExcelWriterFactory(String filePath) {excelWriter = EasyExcel.write(filePath).build();}/*** 链式模板表头写入** @param headClazz 表头格式* @param data      数据 List<ExcelModel> 或者List<List<Object>>* @return*/public EasyExcelWriterFactory writeModel(Class headClazz, List data, String sheetName) {excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(headClazz).build());return this;}/*** 链式自定义表头写入** @param head* @param data      数据 List<ExcelModel> 或者List<List<Object>>* @param sheetName* @return*/public EasyExcelWriterFactory write(List<List<String>> head, List data, String sheetName) {excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(head).build());return this;}/*** 使用此类结束后,一定要关闭流*/public void finish() {excelWriter.finish();}
}

3.ExcelUtil

import lombok.extern.slf4j.Slf4j;import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;/*** @className com.yq.common.utils.ExcelUtil* @author: yangjie* @description: ExcelUtil*/
@Slf4j
public class ExcelUtil {/*** 下载Excel** @param response 请求response* @param fileName 下载文件名称 xxx.xlsx* @param filePath 下载文件路径 D://xxx/xxx*/public static void downExcel(HttpServletResponse response, String fileName, String filePath) {// path是指想要下载的文件的路径File file = new File(filePath);ExcelUtil.downExcel(response, fileName, file);}/*** 下载Excel** @param response 请求response* @param fileName 下载文件名称 xxx.xlsx* @param file     下载文件流*/public static void downExcel(HttpServletResponse response, String fileName, File file) {FileInputStream fileInputStream = null;InputStream fis = null;OutputStream outputStream = null;try {// 将文件写入输入流fileInputStream = new FileInputStream(file);fis = new BufferedInputStream(fileInputStream);byte[] buffer = new byte[fis.available()];fis.read(buffer);fis.close();// 清空responseresponse.reset();// 设置response的Header// 解决跨域问题,这句话是关键,对任意的域都可以,如果需要安全,可以设置成安前的域名response.addHeader("Access-Control-Allow-Origin", "*");response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");response.setHeader("FileName", URLEncoder.encode(fileName, "UTF-8"));response.setHeader("Access-Control-Expose-Headers", "FileName");response.setCharacterEncoding("UTF-8");//Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存//attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));// 告知浏览器文件的大小response.addHeader("Content-Length", "" + file.length());outputStream = new BufferedOutputStream(response.getOutputStream());response.setContentType("application/octet-stream");outputStream.write(buffer);outputStream.flush();} catch (IOException e) {e.printStackTrace();log.error("文件下载异常,{}", e);} finally {if (fileInputStream != null) {try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}
}

4.使用方式

实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class AiAssistantForecastVo implements Serializable {private static final long serialVersionUID = -2986024804753822180L;@ExcelProperty(value = "细分品类", index = 0)private String presentName;@ExcelProperty(value = "大区", index = 1)private String regionNo;@ExcelProperty(value = "管理城市", index = 2)private String managingCityNo;@ExcelProperty(value = "店铺", index = 3)private String organKey;@ExcelProperty(value = "指标", index = 4)private String indicator;@ExcelProperty(value = "202336", index = 5)private String naturalYearWeek202336;
}

调用:

String fileName = "xxxx_" + System.currentTimeMillis() + ".xlsx";
String tempPath = "/home/uploads/";
String filePath = tempPath + fileName;
EasyExcelWriterFactory res = EasyExcelUtil.writeWithSheets(filePath)
.writeModel(AiAssistantForecastVo.class, aiAssistantForecastVoList, "报表数据");
res.finish();
ExcelUtil.downExcel(response, fileName, filePath);

5.如何处理不确定的字段?

  1. 通过反射拿到字段。
  2. 拿到字段后可以拿字段的注解。
  3. 根据字段注解的属性值可以确定需要填充的是哪个动态的字段。
  4. 通过使用 Reflect 反射工具类,很好的动态的填充了属性值。
//判断,周数相等的时候,才能塞进去
final Integer naturalYear = forecast.getNaturalYear();
final Integer naturalYearWeek = forecast.getNaturalYearWeek();
// 获取类的所有字段
Field[] fields = aiAssistantForecastVo.getClass().getDeclaredFields();
// 遍历字段
for (Field field : fields) {field.setAccessible(true);// 判断字段上是否有指定的注解if (field.isAnnotationPresent(ExcelProperty.class)) {// 获取字段上的注解ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);// 获取注解的属性值final String[] value = annotation.value();final String property = value[0];if (StringUtils.equals(property, naturalYear.toString() + naturalYearWeek.toString())) {try {field.set(aiAssistantForecastVo, Objects.nonNull(Reflect.on(forecast).field(mapValue).get()) ? Reflect.on(forecast).field(mapValue).get().toString() : "");} catch (IllegalAccessException e) {e.printStackTrace();}}}
}

6.需要注意的点

  • 如果是 docker 部署,记得设置挂载地址/home/uploads/
#docker run的时候设置挂载地址
-v /home/uploads:/home/uploads
  • 下载时间过长,可能需要设置 nginx 的超时时间
http {proxy_connect_timeout 300; #单位秒proxy_send_timeout 300; #单位秒proxy_read_timeout 300; #单位秒proxy_buffer_size 16k;proxy_buffers 4 64k;proxy_busy_buffers_size 128k;proxy_temp_file_write_size 128k;}

觉得有用的话点个赞 👍🏻 呗。
❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/157886.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【开源】基于Vue和SpringBoot的教学过程管理系统

项目编号&#xff1a; S 054 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S054&#xff0c;文末获取源码。} 项目编号&#xff1a;S054&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 教师端2.2 学生端2.3 微信小程序端2…

8 个有效的安卓数据恢复软件——可让丢失的文件起死回生!

所有数字设备最终都会失败。安卓设备也不例外&#xff0c;无论您使用的是 Android 手机还是平板电脑。由于缺乏备份、意外删除、存储卡问题、生根错误等&#xff0c;您可能会丢失一些宝贵的数据。 如果发生这种情况&#xff0c;最好的选择之一是使用安卓数据恢复软件——这可能…

Xilinx Zynq-7000系列FPGA任意尺寸图像缩放,提供两套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐FPGA图像处理方案FPGA图像缩放方案 3、设计思路详解HLS 图像缩放介绍 4、工程代码1&#xff1a;图像缩放 HDMI 输出PL 端 FPGA 逻辑设计PS 端 SDK 软件设计 5、工程代码2&#xff1a;图像缩放 LCD 输出PL 端 FPGA 逻辑设计PS 端 SDK 软件设…

漏洞检测与EPSS评分

EPSS (利用预测评分系统)是为了测量特定的漏洞在野外被利用的可能性。EPSS 得分范围从0% (最低的利用概率)到100% (最高的利用概率)。此外&#xff0c;由于仅从概率得分很难推断出真正的意义&#xff0c;EPSS 还提供百分位排名; 百分位排名衡量 EPSS 概率相对于所有其他 EPSS 得…

事关Django的静态资源目录设置与静态资源文件引用(Django的setting.py中的三句静态资源(static)目录设置语句分别是什么作用?)

在Django的setting.py中常见的三句静态资源(static)目录设置语句如下&#xff1a; STATICFILES_DIRS [os.path.join(BASE_DIR, static_list)] # 注意这是一个列表,即可以有多个目录的路径 STATIC_ROOT os.path.join(BASE_DIR, static_root) STATIC_URL /static-url/本文介…

PCS7中如何实现DB块变量的自动上传

问题:如何实现PCS7中DB块中变量的自动上传? 解答:PCS7下,所有CFC中的变量都通过编译的方式自动上传的OS项目中,针对自定义的DB块同样也可以通过设置相关属性自动上传的OS中,具体操作如下: 插入一个全局数据块。 注意:数据块号必须符合要求,可以参考PCS7中定义的预留DB…

【代数学习题4.1】从零理解范数与迹 —— 求极小多项式

从零理解范数与迹 —— 求极小多项式 写在前面概念解释题目解答 1. 极小多项式极小多项式的求法1. 对 α \alpha α 的极小多项式python求解 2. 对 α 1 \alpha 1 α1 的极小多项式python找到多项式python找到极小多项式 3. 对 α 2 α 1 \alpha^2 \alpha 1 α2α1 的…

Android : ListView + BaseAdapter-简单应用

​​容器与适配器&#xff1a;​​​​​ http://t.csdnimg.cn/ZfAJ7 示例图&#xff1a; 实体类 News.java package com.example.mylistviewbaseadapter.entity;public class News {private String title;private String content;private int img;public News(Str…

【Linux】Linux中的基本概念

Linux中的基本概念 1. 路径分隔符/2. 当前目录 .3. 返回上级目录 . .目录结构&#xff1a;多叉树 4. 路径5. 路径 { 绝对路径 相对路径 }6. * 通配符 指定路径下的所有文件7. 同级目录下&#xff0c;不允许存在同名文件&#xff0c;或者同名目录8. 命令的本质就是可执行文件9…

第三方模块远程注入到软件中引发软件异常的若干实战案例分享

目录 1、概述 2、老版本的输入法导致软件CPU频繁跳高&#xff08;导致软件出现卡顿&#xff09;的问题 3、QQ拼音输入法注入到安装包进程中&#xff0c;导致安装包主线程卡死问题 3.1、多线程死锁分析 3.2、进一步研究 4、安全软件注入到软件中&#xff0c;注入模块发生了…

什么是图神经网络

当这两种技术融合在一起时&#xff0c;就可以创造出一些新颖、奇妙的东西——比如手机和浏览器融合在一起&#xff0c;产生了智能手机。 如今&#xff0c;科研人员正在将人工智能发现模式的能力应用于存储各种数据点之间关系信息的大型图数据库。与此同时&#xff0c;就产生了…

FreeRTOS源码阅读笔记4--semphr.h

信号量是特殊的队列--无法存储消息的队列&#xff0c;相关的接口函数声明在semphr.h中&#xff0c;通过宏定义替换队列函数实现。 4.1创建二值信号量xSemaphoreCreateBinary() 4.1.1函数原型 queueQUEUE_TYPE_BINARY_SEMAPHORE&#xff1a;一个宏&#xff0c;表示创建队列的…

这是一棵适合搜索二叉树

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;强烈推荐优质专栏: &#x1f354;&#x1f35f;&#x1f32f;C的世界(持续更新中) &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;…

单链表OJ题--9.环形链表

9.环形链表 141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; /* 解题思路&#xff1a; 定义快慢指针fast,slow, 如果链表确实有环&#xff0c;fast指针一定会在环内追上slow指针。 */typedef struct ListNode Node; bool hasCycle(struct ListNode *head) {Node* slow …

深信服技术认证“SCSA-S”划重点:渗透测试工具使用

为帮助大家更加系统化的学习网络安全知识&#xff0c;尽快通过深信服安全服务认证工程师认证&#xff0c;深信服推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 深信服安全服务认证工程师&#xff08;…

【开源】基于Vue和SpringBoot的创意工坊双创管理系统

项目编号&#xff1a; S 049 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S049&#xff0c;文末获取源码。} 项目编号&#xff1a;S049&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员端2.2 Web 端2.3 移动端 三、…

紧跟热点:教你如何快速掌握ChatGPT

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

图解Spark Graphx基于connectedComponents函数实现连通图底层原理

原创/朱季谦 第一次写这么长的graphx源码解读&#xff0c;还是比较晦涩&#xff0c;有较多不足之处&#xff0c;争取改进。 一、连通图说明 连通图是指图中的任意两个顶点之间都存在路径相连而组成的一个子图。 用一个图来说明&#xff0c;例如&#xff0c;下面这个叫graph…

【教3妹学编程-算法题】最大异或乘积

3妹&#xff1a;2哥&#xff0c;你有没有看到新闻“18岁父亲为4岁儿子落户现身亲子鉴定” 2哥 : 啥&#xff1f;18岁就当爹啦&#xff1f; 3妹&#xff1a;确切的说是14岁好吧。 2哥 : 哎&#xff0c;想我30了&#xff0c; 还是个单身狗。 3妹&#xff1a;别急啊&#xff0c; 2…