Easy Excel从入门到精通!!!

目录

1.文件导入

1.1基本方式读取excel文件内容

1.2注解+模型映射器读取excel

1.3多行表头读取

1.4文件上传读取

2.文件导出

2.1基本方式导出

2.2模型映射导出

2.3设置行高、列宽等内容

2.4合并单元格

2.5导出设置超链接、批注、公式

2.6模板填充对象导出

2.7模板填充对象列表导出

2.8模板组合填充

3.文件下载

EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。
他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能

1.文件导入

1.1基本方式读取excel文件内容

read为读取,sheet就是指定excel文件中第几个sheet页,doReadnSync为结尾方法

//指定位置读取excel内容@Testpublic void test01(){File file = new File("C:\\Users\\Lenovo\\Desktop\\excel\\user-list.xlsx");List<Object> objects = EasyExcel.read(file).sheet(0).doReadSync();for (Object object : objects) {log.info("读取内容为{}",object);}}//输入流读取@Testpublic void test02(){InputStream inputStream = EazyExcelTest.class.getClassLoader().getResourceAsStream("user-list.xlsx");List<Map<Integer, Object>> list = EasyExcel.read(inputStream).sheet(0).doReadSync();for (Map<Integer, Object> item : list) {log.info("昵称: {}, 性别: {}, 生日: {}, 邮箱: {}, 积分: {}", item.get(0), item.get(1), item.get(2), item.get(3), item.get(4));}}

1.2注解+模型映射器读取excel

在上面中读取的excel返回的其实是个Map其中key为下标value为单元格数据,但是如果通过map.get()方式进行获取就太麻烦了,所以我们可以定义一个类让他去和excel文件数据进行映射这样在去操作对象就方便多了

表格为

实体类为

其中的注解起到的作用就是进行与excel表格字段进行映射下面我一一来说

@ExcelProperty(value = " "),这个注解是最基本的通过它与表格字段进行映射

@DateTimeFormat(value = "年月日时分秒"),这个注解负责和表格的日期字段进行映射

@NumberFormat(value = " ")这个注解负责数字转换,用String去接收excel数字格式的数据会调用这个注解,如果想什么结尾就在结尾加上例如%

@Data
public class UserInfoModel {/*** 昵称。*/@ExcelProperty(value = "昵称")private String userName;/*** 性别。*/@ExcelProperty(value = "性别",converter = UserInfoGenderConverter.class)private Integer userGender;/*** 生日。*/@ExcelProperty(value = "生日")@DateTimeFormat(value = "yyyy-MM-dd")private String userBirth;/*** 邮箱。*/@ExcelProperty(value = "邮箱")private String userEmail;/*** 积分。*/@ExcelProperty(value = "积分")private Integer userScore;/*** 排名。*/@ExcelProperty(value = "排名")@NumberFormat(value = "#.##%")private String userRank;
}

我们看到在注解@ExcelProperty(value =" ") 后面有个convert他的作用就是因为字段男女是单一的在excel中,我们想在程序中用数字表示就可以自定义一个模型映射器进行转换,代码如下,实现converter指定类型然后做判断返回

/*** 用户信息,性别,转换器。*/
public class UserInfoGenderConverter implements Converter<Integer> {public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {String valuee = cellData.getStringValue();switch (valuee) {case "男":return 1;case "女":return 2;default:return 0;}}
}

读取代码

  @Testpublic void test3() {File file = new File("C:\\Users\\Lenovo\\Desktop\\excel\\user-list.xlsx");List<UserInfoModel> list = EasyExcel.read(file).sheet(0).head(UserInfoModel.class).doReadSync();for (UserInfoModel item : list) {log.info("昵称: {}, 性别: {}, 生日: {}, 邮箱: {}, 积分: {},排名{}", item.getUserName(), item.getUserGender(), item.getUserBirth(), item.getUserEmail(), item.getUserScore(),item.getUserRank());}}

1.3多行表头读取

开发中有可能用户上传的表格是从第几行之后才是正式的数据,而我们在代码中默认的就是从第一行进行读取,遇到这种情况我们可以指定从第几行读取,通过headRowNumber进行实现

实例

代码

@Testpublic void test4() {File file = new File("C:\\Users\\Lenovo\\Desktop\\excel\\user-list2.xlsx");List<UserInfoModel> list = EasyExcel.read(file).headRowNumber(6).sheet(0).head(UserInfoModel.class).doReadSync();for (UserInfoModel item : list) {log.info("昵称: {}, 性别: {}, 生日: {}, 邮箱: {}, 积分: {},排名{}", item.getUserName(), item.getUserGender(), item.getUserBirth(), item.getUserEmail(), item.getUserScore(), item.getUserRank());}}

1.4文件上传读取

通过ApiPost上传文件,后台进行读取处理

@RestController
@RequestMapping("/home")
public class HomeController {@PostMapping("/upload")@SneakyThrowspublic List<UserInfoModel> upload(@RequestPart("file") MultipartFile file) {InputStream inputStream = file.getInputStream();List<UserInfoModel> list = EasyExcel.read(inputStream).head(UserInfoModel.class).headRowNumber(6).sheet(0).doReadSync();return list;}
}

2.文件导出

2.1基本方式导出

首先我们需要新建一张表用于导出时指定位置,下面演示导出三种数据方式;write指定要导出位置的excel表,sheet指定sheet名称dowrite指定导出的数据

 /*** 导出路径。*/private final String EXPORT_PATH = "C:\\Users\\Lenovo\\Desktop\\excel\\eazyExcel.xlsx";/*** 演示:List<List<Object>> 方式导出。*/@Testpublic void test01(){List<Object> list1 = Arrays.asList("郭德纲",28,"男");List<Object> list2 = Arrays.asList("郭麒麟",30,"男");List<List<Object>> asList = Arrays.asList(list1, list2);EasyExcel.write(EXPORT_PATH).sheet("export").doWrite(asList);}/*** Map导出*/@Testpublic void test02(){Map<Integer,Object> map1 = new HashMap<>();map1.put(0,"郭德纲");map1.put(1,28);map1.put(2,"男");Map<Integer,Object> map2 = new HashMap<>();map2.put(0,"郭麒麟");map2.put(1,60);map2.put(2,"男");List<Map<Integer, Object>> asList = Arrays.asList(map1, map2);EasyExcel.write(EXPORT_PATH).sheet("export1").doWrite(asList);}/*** 对象导出*/@Test@SneakyThrowspublic void test03(){UserInfoModel userInfoModel1 = new UserInfoModel();userInfoModel1.setUserBirth(DateUtils.parseDate("1997-06-29"));userInfoModel1.setUserName("郭德纲");userInfoModel1.setUserGender(28);UserInfoModel userInfoModel2 = new UserInfoModel();userInfoModel2.setUserBirth(DateUtils.parseDate("1997-06-29"));userInfoModel2.setUserName("郭麒麟");userInfoModel2.setUserGender(28);OrderInfoModel orderInfoModel = new OrderInfoModel();orderInfoModel.setOrderTime(DateUtils.parseDate("1987-06-28"));orderInfoModel.setOrderTitle("订单标题");orderInfoModel.setOrderPrice(new BigDecimal("10"));List<Object> list = Arrays.asList(userInfoModel1, userInfoModel2,orderInfoModel);EasyExcel.write(EXPORT_PATH).sheet("export2").doWrite(list);}

2.2模型映射导出

在开发中我们通常是根据前端传过来的参数去DB中查询出对应数据封装成对象集合的形式然后导出

模拟获取数据

 /*** 获取用户信息列表。** @return 返回结果。*/@SneakyThrowsprivate List<UserInfoModel> getList() {UserInfoModel user1 = new UserInfoModel();user1.setUserName("郭德纲"); // 用户姓名。user1.setUserGender(1); // 用户性别。user1.setUserBirth(DateUtils.parseDate("1973-01-18")); // 用户生日。user1.setUserScore(100); // 用户积分。user1.setUserReward(BigDecimal.valueOf(123.45)); // 用户佣金。UserInfoModel user2 = new UserInfoModel();user2.setUserName("于谦"); // 用户姓名。user2.setUserGender(2); // 用户性别。user2.setUserBirth(DateUtils.parseDate("1967-12-06")); // 用户生日。user2.setUserScore(200); // 用户积分。user2.setUserReward(BigDecimal.valueOf(234.56)); // 用户佣金。UserInfoModel user3 = new UserInfoModel();user3.setUserName("岳云鹏"); // 用户姓名。user3.setUserGender(0); // 用户性别。user3.setUserBirth(DateUtils.parseDate("1985-09-17")); // 用户生日。user3.setUserScore(300); // 用户积分。user3.setUserReward(BigDecimal.valueOf(345.67)); // 用户佣金。return Arrays.asList(user1, user2, user3);}

实体类

在导入时我们会根据excel文件的表头映射成实体类一一对应然后将list集合进行数据操作,同理导出也是一样的我们查询出的数据映射到实体类然后设置表头这样就能导出excel文件了,这里的注解和上面导入一样,其中我们可以在@ExcelProperty注解中通过{ }将字段汇总到一个表头下,@ExclIgnore注解的作用就是排除掉这个字段不让他在表格中出现

/*** 用户信息,模型。*/
@Data
public class UserInfoModel implements Serializable {private static final long serialVersionUID = 1L;/*** 用户姓名。*/@ExcelProperty(value = {"基本信息","用户姓名"})private String userName;/*** 用户性别。*/@ExcelProperty(value = {"基本信息","用户性别"},converter = UserInfoModelConvert.class)private Integer userGender;/*** 用户生日。*/@ExcelProperty(value = {"基本信息","用户生日"})@DateTimeFormat(value = "yyyy年MM月dd日")private Date userBirth;/*** 用户积分。*/@ExcelProperty(value = "用户积分")private Integer userScore;/*** 用户佣金。*/@ExcelProperty(value = "用户佣金")@NumberFormat(value = "$#.##")@ExcelIgnoreprivate BigDecimal userReward;}

导出代码

导出excludeColumnFieldNames的意思是排除调那些字段,includeColumnFieldNames是只要那些字段

  public static final String EXPORT_FILE = "C:\\Users\\Lenovo\\Desktop\\excel\\eazyExcel.xlsx";/*** 测试:模型映射导出。*/@Test@SneakyThrowspublic void testExport() {// 获取:用户信息列表。List<UserInfoModel> list = this.getList();// 处理:导出数据。EasyExcel.write(EXPORT_FILE).head(UserInfoModel.class).sheet("sheet01").excludeColumnFieldNames(Arrays.asList("userScore")).doWrite(list);}

2.3设置行高、列宽等内容

@ContentRowHeight(value = 20) //行高(内容)只能加在类上
@HeadRowHeight(value = 20) //行高 (标题)只能加在类上
@ColumnWidth(value = 20) //列宽 可以加在类上也可以加在字段上

导出代码

其中.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) //自适应表格格式

 /*** 测试:模型映射导出。*/@Test@SneakyThrowspublic void testExport() {// 获取:用户信息列表。List<UserInfoModel> list = this.getList();// 处理:导出数据。EasyExcel.write(EXPORT_FILE).sheet("导出数据").head(UserInfoModel.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) //自适应格式.doWrite(list);}

实体类

/*** 用户信息,模型。*/
@Data
@ContentRowHeight(value = 20) //行高(内容)
@HeadRowHeight(value = 20) //行高 (标题)
public class UserInfoModel implements Serializable {private static final long serialVersionUID = 1L;/*** 用户姓名。*/@ExcelProperty(value = {"基本信息", "用户姓名"})private String userName;/*** 用户性别。*/@ExcelProperty(value = {"基本信息", "用户性别"})private Integer userGender;/*** 用户生日。*/@ExcelProperty(value = {"基本信息", "用户生日"})@DateTimeFormat(value = "yyyy年MM月dd日")@ColumnWidth(value = 20)private Date userBirth;/*** 用户积分。*/@ExcelProperty(value = {"账户信息", "用户积分"})private Integer userScore;/*** 用户佣金。*/@ExcelProperty(value = {"账户信息", "用户佣金"})@NumberFormat(value = "¥#.##")private BigDecimal userReward;}

2.4合并单元格

合并单元格也很常见,共有两种方式

方式1:注解合并

@OnceAbsoluteMerge加在类上指定某个单元格进行合并

@ContentLoopMerge加在某个表头下指定数量进行循环合并

/*** 用户信息,模型。*/
@Data
//@OnceAbsoluteMerge(firstRowIndex = 2, lastRowIndex = 3, firstColumnIndex = 0, lastColumnIndex = 0) //单次指定某个单元格合并
//firstRowIndex = 起始位置为0 2的话也就是第三行的位置, lastRowIndex = 3其实位置为0就是firstRowIndex到3的位置进行合并, firstColumnIndex = 0第几列, lastColumnIndex = 0第几列结束
public class UserInfoModel implements Serializable {private static final long serialVersionUID = 1L;/*** 团队名称。*/@ExcelProperty(value = "团队名称")@ContentLoopMerge(eachRow = 2) //循环合并就是自动合并,在团队名称单元格下面自动进行每两个单元格进行合并,但是不智能可能会多出来一行private String teamName;

方式2:代码合并

.registerWriteHandler(new OnceAbsoluteMergeStrategy(0,1,4,4))//指定单元格进行合并一次
.registerWriteHandler(new LoopMergeStrategy(2,0))//循环合并
@Test@SneakyThrowspublic void test() {EasyExcel.write(EXPORT_FILE).head(UserInfoModel.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).registerWriteHandler(new OnceAbsoluteMergeStrategy(0,1,4,4))//指定单元格进行合并一次.registerWriteHandler(new LoopMergeStrategy(2,0))//循环合并.sheet("sheet1").doWrite(this::getList);}

2.5导出设置超链接、批注、公式

一句话概括就是在你要设置的字段上加上原始数据类型WriteCellData<类型>,然后在代码中进行添加就可以了,如下所示;导出公式不常用这里不做演示

模型类

/*** 用户信息,模型类。*/
@Data
@ColumnWidth(value = 20)
public class UserInfoModel implements Serializable {private static final long serialVersionUID = 1L;/*** 行号。*/@ExcelIgnore@ExcelProperty(value = "行号")private Integer rowNumber;/*** 用户标识。*/@ExcelProperty(value = "用户标识")private WriteCellData<Long> id;/*** 用户昵称。*/@ExcelProperty(value = "用户昵称")private WriteCellData<String> userNickName;/*** 用户性别。*/@ExcelProperty(value = "用户性别")private Integer userGender;/*** 用户生日。*/@ExcelProperty(value = "用户生日")private Date userBirth;/*** 用户年龄。*/@ExcelIgnore@ExcelProperty(value = "用户年龄")private Integer userAge;}

导出代码

  @Testpublic void test() {List<UserInfoEntity> list = userInfoService.list();List<UserInfoModel> modelList = list.stream().map(item -> {UserInfoModel userInfoModel = new UserInfoModel();//添加批注CommentData commentData = new CommentData();commentData.setAuthor("我是作者李佳伟");commentData.setRichTextStringData(new RichTextStringData("这是批注内容"));WriteCellData<Long> id = new WriteCellData<>(new BigDecimal(item.getId()));id.setCommentData(commentData);userInfoModel.setId(id);//添加超链接HyperlinkData hyperlinkData = new HyperlinkData();hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.URL);hyperlinkData.setAddress("http://www.baidu.com");WriteCellData<String> userNickName = new WriteCellData<>(item.getUserNickname());userNickName.setHyperlinkData(hyperlinkData);userInfoModel.setUserNickName(userNickName);userInfoModel.setUserGender(item.getUserGender());userInfoModel.setUserBirth(item.getUserBirth());return userInfoModel;}).collect(Collectors.toList());EasyExcel.write(EXPORT_FILE).head(UserInfoModel.class).sheet().registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).doWrite(modelList);}

2.6导出图片到Excel

导出图片到Excel的需求很常见,有多种方法下面我将一一演示

模型类

如果你想通过获取图片的file、byte、流、Sting的方式获取图片然后导出到excel就分别指定他们的类型,但是这种方式不常见,因为图片一般我们都是存储在OSS中然后我们通过图片的URL来进行访问图片,所以我们指定图片为URL类型,还有一种情况存储在数据库的图片URL为Base64格式的,那么我们需要进行转换

/*** 用户信息,模型。*/
@Data
@ContentRowHeight(value = 50)
@ColumnWidth(value = 30)
public class UserInfoModel implements Serializable {private static final long serialVersionUID = 1L;@ExcelProperty(value = "File类型")private File fileImage;@ExcelProperty(value = "byte[]类型")private byte[] byteImage;@ExcelProperty(value = "InputStream类型")private InputStream inputStreamImage;@ExcelProperty(value = "URL类型")private URL urlImage;@ExcelProperty(value = "String类型", converter = Base64Convert1.class)private String stringImage;}

导出代码

如果图片通过File、byte、流则直接指定位置获取即可,如果是URL这种最常见则newURL进行指定导出、如果是通过String则需要通过convert转换成字节其实也是通过File获取然后转换这样,如果数据库图片的URL是Base64的那么也很简单我们只需自定义一个Convert,将Base64转成字节在将字节通过ConvertString类型进行返回就可以了;或者存到DB的是Base64编码后的URL然后我们可以查询出来转成URL在进行导出图片到excel

public static final String EXPORT_FILE = "C:\\Users\\Lenovo\\Desktop\\excel\\eazyExcel.xlsx";@Test@SneakyThrowspublic void test() {UserInfoModel userInfoModel = new UserInfoModel();//文件类型userInfoModel.setFileImage(new File("C:\\Users\\Lenovo\\Desktop\\excel\\20201124032511.png"));//byte类型userInfoModel.setByteImage(FileUtils.readFileToByteArray(new File("C:\\Users\\Lenovo\\Desktop\\excel\\20201124032511.png")));//输入流类型userInfoModel.setInputStreamImage(FileUtils.openInputStream(new File("C:\\Users\\Lenovo\\Desktop\\excel\\20201124032511.png")));//URL类型userInfoModel.setUrlImage(new URL("https://img-home.csdnimg.cn/images/20201124032511.png"));//String类型userInfoModel.setStringImage("iVBORw0KGgoAAAANSUhEUgAAAKAAAABYCAYAAAByDvxZAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAoKADAAQAAAABAAAAWAAAAADfqAIVAAAeZklEQVR4Ae1cB3hUx7W");EasyExcel.write(EXPORT_FILE).head(UserInfoModel.class).sheet().registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).doWrite(Collections.singletonList(userInfoModel));}@SneakyThrows@Testpublic void testBase64(){byte[] bytes = FileUtils.readFileToByteArray(new File("C:\\Users\\Lenovo\\Desktop\\excel\\20201124032511.png"));String base64 = Base64.getEncoder().encodeToString(bytes);System.out.println(base64);}

2.6模板填充对象导出

我们上面学习到了通过模型类指定单元格进行导出,然后指定他的宽高、格式等等,那么这种方式大家发现是不是很麻烦,还需要去指定格式等等;现在我们可以在resouce目录下创建一个模板文件然后我们在模板中设置好格式,然后将数据填充到模板中这样就方便多了,这种方式适合简单的导出没什么要求,但是如果是出现一些字段上的需求比如超链接、批注、金钱符号等就无法满足。

模板样式

模型类

/*** 用户信息,模型。*/
@Data
public class UserInfoModel implements Serializable {private static final long serialVersionUID = 1L;/*** 用户编号。*/private Integer userCode;/*** 用户昵称。*/private String userNickname;/*** 用户积分。*/private Integer userScore;/*** 用户佣金。*/private BigDecimal userReward;}

导出代码

    /*** 对象方式*/@Testpublic void test1(){InputStream inputStream = EasyExcelTest.class.getClassLoader().getResourceAsStream("export-template.xlsx");UserInfoModel userInfoModel = new UserInfoModel();userInfoModel.setUserCode(1001);userInfoModel.setUserNickname("李佳伟");userInfoModel.setUserScore(200);userInfoModel.setUserReward(BigDecimal.valueOf(123.45));EasyExcel.write(EXPORT_FILE).withTemplate(inputStream).sheet().doFill(userInfoModel);}

导出样式

2.7模板填充对象列表导出

上面我们是添加一个对象然后导出,但是实际业务是多条数据的所以我们要实现添加列表的方式,与单个对象导出不同点就是模板中多加了个.

2.8模板组合填充

在开发中我们可能会遇到往一个表格中添加数据但是表格中有多个表的情况如图所示,然后我们还可能遇到横向填充的情况,这里为什么加个前缀dept或者user呢是为了在填充模型数据时好做区分

我们先获取excelWriter获取操作对象,然后获取添加的表writeSheet,如果是填充一个对象则直接fill如果是多个对象则需要new FillWrapper然后指定你填填充的前缀加数据,然后添加表writerSheet如果需要横向填充的话那就需要手动设置一个进行填充

FillConfig config = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); //横向填充
  @Testpublic void test() {InputStream exportTemplate = EasyExcelTest.class.getClassLoader().getResourceAsStream("export-template.xlsx");try (ExcelWriter excelWriter = EasyExcel.write(EXPORT_FILE).withTemplate(exportTemplate).build()) {WriteSheet writeSheet = EasyExcel.writerSheet().build();// 公司信息。Map<String, Object> companyInfo = new HashMap<>();companyInfo.put("companyName", "北京XXX信息技术有限公司");excelWriter.fill(companyInfo, writeSheet);Map<String, Object> deptMap1 = new HashMap<>();deptMap1.put("deptName", "研发部");deptMap1.put("deptMaster","李佳伟");deptMap1.put("deptContact","17045454589");Map<String, Object> deptMap2 = new HashMap<>();deptMap2.put("deptName", "财务部");deptMap2.put("deptMaster","刘晓宇");deptMap2.put("deptContact","17045454589");FillConfig config = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); //横向填充List<Map<String, Object>> asList = Arrays.asList(deptMap1, deptMap2);excelWriter.fill(new FillWrapper("dept",asList), config, writeSheet);Map<String, Object> userInfo1 = new HashMap<>();userInfo1.put("userCode", 1001);userInfo1.put("userNickname", "张三");userInfo1.put("userScore", 100);userInfo1.put("userReward", BigDecimal.valueOf(123.45));Map<String, Object> userInfo2 = new HashMap<>();userInfo2.put("userCode", 1002);userInfo2.put("userNickname", "李四");userInfo2.put("userScore", 100);userInfo2.put("userReward", BigDecimal.valueOf(123.45));List<Map<String, Object>> mapList = Arrays.asList(userInfo1, userInfo2);excelWriter.fill(new FillWrapper("user",mapList),writeSheet);}}

3.文件下载

文件下载其实我们在导出的时候通常会返回给前端excel的URL下载地址,然后前端直接根据这个URL进行下载就好了我们不用去管他,但是有时候有一些需求需要单独弄一个下载按钮,而不是直接导出的时候就下载了那么也就是如果单独弄下载按钮我们导出就不用返回URL了,直接提示用户稍后去下载中心查看下载就好了。

这里提供两种下载方式,一个是不提供URL进行下载就是可以通过前端传的数据然后去数据库查询出对应数据然后下载,那么如果这种方式也就相当于导出和下载结合了然后写到response里面,另外一种是通过URL进行下载这个URL就是存储在OSS网络的表格URL前端直接通过URL进行下载

未提供URL下载方式相当于导出下载结合

   @GetMapping(value = "/export")@SneakyThrowspublic void export(HttpServletResponse response) {List<UserInfoEntity> list = this.userInfoService.list();response.setCharacterEncoding("UTF-8");response.setContentType("application/vnd.ms-excel");String fileName = URLEncoder.encode("用户信息", "UTF-8");response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");ServletOutputStream outputStream = response.getOutputStream();InputStream exportTemplate = Application.class.getClassLoader().getResourceAsStream("export-template.xlsx");EasyExcel.write(outputStream).withTemplate(exportTemplate).sheet().doFill(list);}

提供URL由前端直接去下载的方式

/*** form表单提交,下载文件流** @param response   HttpServletResponse* @param exportName 文件名* @param exportUrl  下载文件URL*/@RequestMapping(value = "/download", method = RequestMethod.POST)@ResponseBody@PermissionCheck(skipAop = true)public void download(HttpServletResponse response,@RequestParam("exportName") String exportName,@RequestParam("exportUrl") String exportUrl) {// 设置强制下载不打开response.setContentType("application/force-download");response.setHeader("Content-Disposition", "attachment; filename=" + exportName);int byteRead;try {URL url = new URL(exportUrl);URLConnection conn = url.openConnection();conn.setConnectTimeout(3 * 1000);try (InputStream inStream = conn.getInputStream();OutputStream outputStream = response.getOutputStream();) {byte[] buffer = new byte[1204];while ((byteRead = inStream.read(buffer)) != -1) {outputStream.write(buffer, 0, byteRead);}outputStream.flush();}} catch (Exception e) {log.warn("download下载失败!", e);}}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

数据集-目标检测系列-火车检测数据集 train >> DataBall

数据集-目标检测系列-火车检测数据集 train >> DataBall 数据集-目标检测系列-火车检测数据集 数据量&#xff1a;1W 想要进一步了解&#xff0c;请联系 DataBall。 DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;不断增加…

K8s Calico替换为Cilium,以及安装Cilium过程

一、删除Calico kubectl delete daemonset calico-node -n kube-systemkubectl delete deployment calico-kube-controllers -n kube-system kubectl delete ds kube-flannel-ds -n kube-system kubectl delete cm calico-config -n kube-system kubectl delete secret calico…

Docker安装mysql并配置主从,超详细

简介&#xff1a; 本文使用docker安装mysql&#xff0c;并创建master节点&#xff0c;slave节点用于实现主从。废话不多说&#xff0c;直接开始。 1.docker下载镜像&#xff0c;这里我以5.7版本为例。 docker pull mysql:5.7 2.在宿主机上新建如下目录&#xff0c;进行文件挂…

STM32F407单片机编程入门(十四) 内部RTC实时时钟详解及实战含源码

文章目录 一.概要二.RTC基本介绍三.STM32单片机RTC内部结构图四.CubeMX配置一个RTC时间例程五.CubeMX工程源代码下载六.小结 一.概要 RTC&#xff08;Real-Time Clock&#xff09;是一种用于追踪和记录实际时间的时钟系统。在STM32中&#xff0c;RTC通常用于提供实时时钟和日期…

cnn机器学习时python版本不兼容报错

在使用python执行CNN算法时&#xff0c;发生如下报错&#xff1a; A module that was compiled using NumPy 1.x cannot be run in NumPy 2.1.1 as it may crash. To support both 1.x and 2.x versions of NumPy, modules must be compiled with NumPy 2.0. Some module may …

MySQL 中的全文索引:强大的文本搜索利器

《MySQL 中的全文索引&#xff1a;强大的文本搜索利器》 在 MySQL 数据库中&#xff0c;全文索引是一种非常有用的功能&#xff0c;它可以帮助我们快速地在大量文本数据中进行搜索。那么&#xff0c;什么是 MySQL 中的全文索引呢&#xff1f;它又是如何工作的呢&#xff1f;让…

wordpress迁移到别的服务器

wordpress论坛网站搭建 于2023/11/16写的该文章 一-配置环境 配置LNMP&#xff08;linuxnginxmysqlphpphpmyadmin&#xff09;环境或者LAMP&#xff08;apache&#xff09; 可以选择集成了这些软件的套件 下载链接&#xff1a;https://www.xp.cn/download.html 手动下载这…

传输层 IV(TCP协议——流量控制、拥塞控制)【★★★★】

&#xff08;★★&#xff09;代表非常重要的知识点&#xff0c;&#xff08;★&#xff09;代表重要的知识点。 一、TCP 流量控制&#xff08;★★&#xff09; 1. 利用滑动窗口实现流量控制 一般说来&#xff0c;我们总是希望数据传输得更快一些。但如果发送方把数据发送得…

Linux 一些快捷键使用操作技巧

ctrl c : 强制停止 如图仅输入tail命令时程序会卡住&#xff0c;这时就需要强制停止 ctrl d : 退出或者登出 history : 查看历史输入命令 &#xff01;命令 &#xff1a;自动执行上一次匹配前缀的命令 &#xff08;注意不要用这个命令执行太过久远的&#xff0c;容易执行错误…

设置VsCode搜索时排除文件,文件列表中隐藏文件

按照《VsCode gdb gdbserver远程调试C程序》中介绍的方法&#xff0c;配置好VsCode后&#xff0c;打开一个C/C工程&#xff0c;发现左侧的面板会显示编译时生成的中间文件&#xff08;比如.d和.o文件&#xff09;。我们可以通过设置隐藏掉一些我们不需要打开的文件以简洁面板…

C++基础(8.继承_多态)

目录 继承&#xff1a; 继承的概念&#xff1a; 继承的定义&#xff1a; 基类和派生类对象赋值转换 &#xff1a; 继承中的作用域&#xff1a; 派生类的默认成员函数&#xff1a; 例题&#xff1a; ​编辑​编辑​编辑 继承与友元&#xff1a; 继承与静态成员&#xff1…

【C++】C++入门概念(二)

引用 概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。 比如&#xff1a;李逵&#xff0c;在家称为"铁牛"&#xff0c;江湖上人称"黑旋…

汇川AM600 与 codesys opc server 通讯并建立 OPCserver 的方法

AM600 与 codesys opc server 通讯并建立 OPCserver 的方法 AM600 OPC Server 架构 1、程序后台“应用中”添加 symbols config 2、 选择作为 OPCsever 跟 PLC 通讯数据 点击 ACCERSS rights 下的箭头选择权限 安装目录下:打开“OPCConfig”

re题(38)BUUCTF-[FlareOn6]Overlong

BUUCTF在线评测 (buuoj.cn) 运行一下.exe文件 查壳是32位的文件&#xff0c;放到ida反汇编 对unk_402008前28位进行一个操作&#xff0c;我们看到运行.exe文件的窗口正好是28个字符&#xff0c;而unk_402008中不止28个数据&#xff0c;所以猜测MessageBoxA&#xff08;&#x…

MySQL --表的约束

文章目录 1.空属性2.默认值3.列描述4.zerofill5.主键6.自增长7.唯一键8.外键9.综合案例-阅读 1.空属性 两个值&#xff1a;null&#xff08;默认的&#xff09;和not null(不为空) 数据库默认字段基本都是字段为空&#xff0c;但是实际开发时&#xff0c;尽可能保证字段不为空…

AI客服对决:智能客服PK赛揭示企业级AI应用未来

如今的AI对抗场景&#xff0c;简直成了颇具娱乐性的“观赏项目”。围观群众们不仅看得津津有味&#xff0c;时不时还会发出阵阵笑声。 最近&#xff0c;一场别开生面的AI客服挑战赛引发了热议&#xff1a;一位使用“花开富贵”阿姨人设的Agent甩出一条链接&#xff0c;结果对面…

如何在命令执行超时时自动终止该命令

问题 问题 《Command line command to auto-kill a command after a certain amount of time》 中的回答提出了一种从 bash 命令行中为长时间运行的命令设置超时的方法&#xff1a; ( /path/to/slow command with options ) & sleep 5 ; kill $!但是有可能某个“耗时长”…

python绘制弦图-科研作图

一、背景 弦图以其直观、精美的展示方式受到越来越多人的关注&#xff0c;它不仅能够有效展示两个变量之间的联系&#xff0c;还能同时展现多个变量间的复杂互动&#xff0c;本文将通过Python语言中的pycirclize库&#xff0c;带你深入了解如何绘制弦图。 弦图是一种圆…

CQRS模型解析

简介 CQRS中文意思为命令于查询职责分离&#xff0c;我们可以将其了解成读写分离的思想。分为两个部分 业务侧和数据侧&#xff0c;业务侧主要执行的就是数据的写操作&#xff0c;而数据侧主要执行的就是数据的读操作。当然两侧的数据库可以是不同的。目前最为常用的CQRS思想方…

强大的重命名工具 | Bulk Rename Utility v4.0 便携版

软件简介 Bulk Rename Utility是一款功能强大且易于使用的文件批量重命名工具。它不仅体积小巧&#xff0c;而且完全免费&#xff0c;提供了友好的用户界面。该软件允许用户对文件或文件夹进行批量重命名&#xff0c;支持递归操作&#xff0c;即包含子文件夹的重命名。 软件特…