当前位置: 首页 > news >正文

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.7模板填充对象导出

我们上面学习到了通过模型类指定单元格进行导出,然后指定他的宽高、格式等等,那么这种方式大家发现是不是很麻烦,还需要去指定格式等等;现在我们可以在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.8模板填充对象列表导出

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

2.9模板组合填充

在开发中我们可能会遇到往一个表格中添加数据但是表格中有多个表的情况如图所示,然后我们还可能遇到横向填充的情况,这里为什么加个前缀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);}}

相关文章:

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模板填…...

简易CPU设计入门:取指令(三),ip_buf与rd_en的非阻塞赋值

在开篇&#xff0c;还是请大家首先准备好本项目所用的源代码。如果已经下载了&#xff0c;那就不用重复下载了。如果还没有下载&#xff0c;那么&#xff0c;请大家点击下方链接&#xff0c;来了解下载本项目的CPU源代码的方法。 下载本项目代码 准备好了项目源代码以后&…...

【算法】---归并排序(递归非递归实现)

参考 左程云算法 算法导论 前言 本篇介绍 归并排序分治法 前置知识 了解递归&#xff0c; 了解数组。 引入 归并排序 归并排序最早是由公认的现代计算机之父John von Neumann发明的&#xff0c; 这是一种典型的分治思想应用。 我们先介绍分治思想 分治思想 分治思想的…...

UniVue大版本更新:UniVue2.0.0-preview

大版本发布说明 距离上次更新好像已经过去很久了&#xff0c;最近太忙了没时间维护新版本&#xff0c;也是自己在使用的过程中发现了很多问题也有了更多的灵感&#xff0c;由于和之前的版本区别太大&#xff0c;决定重新开一个大版本。这个UniVue2之后的版本追求是性能&#xf…...

RabbbitMQ篇(环境搭建 - 下载 安装)(持续更新迭代)

目录 一、Windows 1. 下载安装程序 2. 安装配置erlang 3. 安装rabbitMQ 4. 验证 二、Linux 1. 下载rpm包 1.1. 下载Erlang的rpm包 1.2. 下载socat的rpm包 1.3. 下载RabbitMQ的rpm包 2. 安装 2.1. 安装Erlang 2.2. 安装socat 2.3. 安装RabbitMQ 3. 启动RabbitMQ服…...

C++基础补充(02)C++其他控制语句break continue goto等

文章目录 1. break2. continue 语句3. goto 语句goto的存在 4. 跳出多重循环4.1 goto 直接跳转4.2 C11及其后版本的 return 语句4.3 使用标志变量 在C中&#xff0c;控制语句用于管理程序的执行流程。常见有 break、continue 和 goto。 1. break break语句主要用于在循环或者s…...

决策树中联合概率分布公式解释说明

学习决策树时书本中有一公式 7-3 是&#xff1a; P ( X x i , Y y j ) p i j ( i 1 , 2 , … , m , j 1 , 2 , … , n ) P(X x_i, Y y_j) p_{ij} \quad (i 1, 2, \dots, m, \ j 1, 2, \dots, n) P(Xxi​,Yyj​)pij​(i1,2,…,m, j1,2,…,n) 这个公式表示的是随机变…...

计算机毕业设计 农场投入品运营管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…...

php email功能实现:详细步骤与配置技巧?

php email发送功能详细教程&#xff1f;如何使用php email服务&#xff1f; 无论是用户注册、密码重置&#xff0c;还是订单确认&#xff0c;电子邮件都是与用户沟通的重要手段。AokSend将详细介绍如何实现php email功能&#xff0c;并提供一些配置技巧&#xff0c;帮助你更好…...

MapBox Android版开发 6 关于Logo

MapBox Android版开发 6 关于Logo Logo的显示查看源码及思路&#xff08;Logo&#xff09;第一步第二步 隐藏Logo示例查看源码及思路&#xff08;Info&#xff09;第一步第二步 隐藏Logo和Info示例 看到有网友留言问如何移除Logo&#xff0c;今天看了下V9源码&#xff0c;发现M…...

2024年房市

24年8月15日&#xff0c;国家统计局公布&#xff0c;“7月末&#xff0c;商品房待售面积73926万平方米”。(原文链接&#xff1a;https://www.stats.gov.cn/sj/zxfb/202408/t20240815_1955982.html)   7.39亿平方存量商品房&#xff0c;估价均价1万每平&#xff0c;总价约&am…...

index索引

index索引&#xff1a; create index 【1】on 【2】(【3】) 1为索引名&#xff0c;通常为id_表名_列名。2为表名。3为列名。 CREATE INDEX id_account_id ON account(id); -- 根据id创建索引 CREATE INDEX id_account_idname on account(id,name); -- 创建组合索引 索…...

理解互联网链路:从本地ISP到Tier 1 ISP运营商

1. 互联网服务提供商&#xff08;ISP&#xff09; 互联网服务提供商&#xff08;ISP&#xff09;是指提供互联网接入服务的公司或组织。它们负责将用户连接到互联网&#xff0c;并提供相关的服务&#xff0c;如电子邮件、网站托管和其他在线服务。ISP可以分为不同的层级&#…...

基于元神操作系统实现NTFS文件操作(三)

1. 背景 本文主要介绍DBR的读取和解析&#xff0c;并提供了基于元神操作系统的实现代码。由于解析DBR的目的是定位到NTFS磁盘分区的元文件$Root进行文件操作&#xff0c;所以只解析了少量的部分&#xff0c;其它部分可以参考相关文档进行理解。 DBR存在于磁盘分区的第一个扇区…...

深度学习与数学归纳法

最近发现&#xff0c;深度学习可以分为两个主要的阶段&#xff0c;分别是前向推理以及反向传播&#xff0c;分别对应着网络的推理和参数训练两个步骤。其中推理有时候也称为归纳推理。 在做参数训练的时候&#xff0c;本质上是在利用历史数据求网络参数的先验分布&#xff1b; …...

《Linux从小白到高手》理论篇(六):Linux软件安装一篇通

List item 本篇介绍Linux软件安装相关的操作命令&#xff0c;看完本文&#xff0c;有关Linux软件安装相关操作的常用命令你就掌握了99%了。 Linux软件安装 RPM RPM软件的安装、删除、更新只有root权限才能使用&#xff1b;查询功能任何用户都可以操作&#xff1b;如果普通用…...

【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错

1. 运行项目 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Appl…...

②EtherCAT转Modbus485RTU网关多路同步高速采集无需编程串口服务器

EtherCAT转Modbus485RTU网关多路同步高速采集无需编程串口服务器https://item.taobao.com/item.htm?ftt&id798036415719 EtherCAT 串口网关 EtherCAT 转 RS485 &#xff08;接上一章&#xff09; 自由协议通信步骤 &#xff08;以MS-A2-1041为例&#xff09; 接收与…...

matlab-对比两张图片的HSV分量的差值并形成直方图

%对比两张图片的HSV分量的差值并形成直方图&#xff0c;改个路径就能用&#xff0c;图片分辨率要一致 close all; clear all; clc; I1imread(E:\test\resources\image\1.jpg); I2imread(E:\test\resources\image\2.jpg); HSV1 rgb2ntsc(I1); HSV2 rgb2ntsc(I2); %HSV,HSV 代…...

微服务SpringGateway解析部署使用全流程

官网地址&#xff1a; Spring Cloud Gateway 目录 1、SpringGateway简介 1、什么是网关 2、为什么用网关【为了转发】 2、应用&#xff1a; 1.启动nacos 2.创建网关项目 3.网关配置1 4.网关配置2【了解】 5.过滤器配置【了解】 1、SpringGateway简介 核心功能有三个&…...

Solidity 存储和内存管理:深入理解与高效优化

在 Solidity 中&#xff0c;存储和内存管理是编写高效智能合约的关键组成部分。合约执行的每一步操作都可能涉及到数据的存储和读取&#xff0c;而这些操作对 gas 的消耗有很大影响。因此&#xff0c;理解 Solidity 的存储模型以及如何优化数据的管理对于合约的安全性、性能和成…...

机器学习篇-day02-KNN算法实现鸢尾花模型和手写数字识别模型

一. KNN简介 KNN思想 K-近邻算法&#xff08;K Nearest Neighbor&#xff0c;简称KNN&#xff09;。比如&#xff1a;根据你的“邻居”来推断出你的类别 KNN算法思想&#xff1a;如果一个样本在特征空间中的k 个最相似的样本中的大多数属于某一个类别&#xff0c;则该样本也属…...

【C++】STL--vector

1.vector的介绍 我们先来看看vector的文档介绍&#xff0c;实际中我们只要熟悉相关接口就好了。 成员函数 使用STL的三个境界&#xff1a;能用&#xff0c;明理&#xff0c;能扩展 &#xff0c;那么下面学习vector&#xff0c;我们也是按照这个方法去学习 2 vector的使用 v…...

Java使用Redis的详细教程

Redis是一个基于内存的key-value结构数据库&#xff0c;即非关系型数据库&#xff0c;具有高性能、丰富的数据类型、持久化、高可用性和分布式等特点。在Java项目中&#xff0c;Redis通常用于缓存、分布式锁、计数器、消息队列和排行榜等场景。以下是在Java中使用Redis的详细教…...

严重 Zimbra RCE 漏洞遭大规模利用(CVE-2024-45519)

攻击者正在积极利用 CVE-2024-45519&#xff0c;这是一个严重的 Zimbra 漏洞&#xff0c;该漏洞允许他们在易受攻击的安装上执行任意命令。 Proofpoint 的威胁研究人员表示&#xff0c;攻击始于 9 月 28 日&#xff0c;几周前&#xff0c;Zimbra 开发人员发布了针对 CVE-2024-…...

php函数积累

对称函数 isset 判断数组arr中是否存在键key 返回值true/false isset(name,$arr) unset 删除数组中的键 需存在key不然抛出异常 unset($arr[name]) json_encode 数据转json格式 json_encode($arr) 一般形式 指定字符编码形式 json_decode json格式转原有数据格式 json_d…...

前端项目场景相关的面试题,包含验证码、图片存储、登录鉴权、动态路由、组件划分等项目场景实际的面试题

项目场景面试题 如何防止短信验证码被刷 问题场景 添加倒计时和图片滑动验证&#xff0c;避免不必要的资源浪费 发送短信验证码需要费用发送短信消耗服务器资源 公司的图片、视频、文件资源如何存储的 传统模式 分开存储到数据服务器&#xff0c;托管服务器到云端 缺点&…...

uniapp 上了原生的 echarts 图表插件了 兼容性还行

插件地址&#xff1a;echarts - DCloud 插件市场 兼容性这块儿不知道后期会不会支持其他浏览器 H5 的话建议可以用原生的不用这个插件...

共享单车轨迹数据分析:以厦门市共享单车数据为例(八)

副标题&#xff1a;基于POI数据的站点综合评价——以厦门市为例&#xff08;三&#xff09; 什么是优劣解距离法&#xff08;TOPSIS&#xff09;&#xff1f; 优劣解距离法&#xff08;Technique for Order Preference by Similarity to Ideal Solution&#xff0c;简称TOPSI…...

sentinel原理源码分析系列(二)-动态规则和transport

本文是sentinel原理源码分析系列第二篇&#xff0c;分析两个组件&#xff0c;动态配置和transport 动态规则 Sentinel提供动态规则机制&#xff0c;依赖配置中心&#xff0c;如nacos&#xff0c;zookeeper&#xff0c;组件支持动态配置&#xff0c;模板类型为规则&#xff0c;支…...

网站建设制作临沂网站建设选盛誉/网站建设与管理就业前景

1.AOP(Aspect-oriented programming ) 面向切面编程 对业务逻辑的各个部分进行隔离&#xff0c;从而使得业务逻辑各部分之间的耦合度降低&#xff0c;提高程序的可重用性&#xff0c;同时提高了开发的效率。 增强产品功能&#xff0c; 解耦&#xff0c; 制定规则 面向规则编程…...

龙岗网站设计资讯/龙斗seo博客

Java中用String类型映射Oracle的Date类型&#xff0c;会类型错误 左右的String类型参数传入Oracle语句中时&#xff0c;必须&#xff0c;转化为Date类型&#xff0c;使用Oracle的to_date()函数 如下 to_date(#{startTime,jdbcTypeTIMESTAMP},yyyy-mm-dd hh24:mi:ss)...

网站开发惠州/劳动局免费培训项目

Python-Dict&Set类型Python的另外两种重要的数据类型Dict和Set&#xff0c;可以快速按照关键字检索信息Dict - 字典list 和 tuple 可以用来表示顺序集合&#xff0c;例如&#xff0c;班里同学的名字&#xff1a;[Adam, Lisa, Bart]或者考试的成绩列表&#xff1a;[95, 85, …...

建筑行业资讯网站/cps推广是什么意思

局域网内的设备越来越多&#xff0c;用ip访问就比较麻烦了。另一方面我们用的公网的dns服务器可能会被投毒。这时候搭建一个本地的DNS服务器&#xff0c;想用什么域名就用什么域名&#xff0c;岂不是很舒服。拿起我们的树莓派&#xff0c;说干就干。准备材料1、树莓派&#xff…...

做网站 乐清/推广赚钱软件

设计师选择了一个字体“汉仪细行楷简”,计算机本身并没有这个字体&#xff0c;需要下载下来才可以展示给用户。 这个时候需要把你自己下载好的字体放在项目文件中&#xff0c;同时重新引入才可以使用外部字体。 结果如下&#xff1a; 字体文件“汉仪细行楷简.ttf”&#xff1a…...

卖东西专业网站网上/软文文案

原文地址&#xff1a;http://www.microsoft.com/china/community/Column/65.mspx利用XML实现通用WEB报表打印卢彦 方案适用性. 1. 远程数据打印。需要打印的数据并不在本地&#xff0c;必须进行远程读取。2. 需要精确控制打印效果&#xff0c;包括页面格式&#xff0c;分页&…...