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的非阻塞赋值
在开篇,还是请大家首先准备好本项目所用的源代码。如果已经下载了,那就不用重复下载了。如果还没有下载,那么,请大家点击下方链接,来了解下载本项目的CPU源代码的方法。 下载本项目代码 准备好了项目源代码以后&…...
【算法】---归并排序(递归非递归实现)
参考 左程云算法 算法导论 前言 本篇介绍 归并排序分治法 前置知识 了解递归, 了解数组。 引入 归并排序 归并排序最早是由公认的现代计算机之父John von Neumann发明的, 这是一种典型的分治思想应用。 我们先介绍分治思想 分治思想 分治思想的…...
UniVue大版本更新:UniVue2.0.0-preview
大版本发布说明 距离上次更新好像已经过去很久了,最近太忙了没时间维护新版本,也是自己在使用的过程中发现了很多问题也有了更多的灵感,由于和之前的版本区别太大,决定重新开一个大版本。这个UniVue2之后的版本追求是性能…...
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中,控制语句用于管理程序的执行流程。常见有 break、continue 和 goto。 1. break break语句主要用于在循环或者s…...
决策树中联合概率分布公式解释说明
学习决策树时书本中有一公式 7-3 是: 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实战项目 附源码+文档+视频讲解
博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…...
php email功能实现:详细步骤与配置技巧?
php email发送功能详细教程?如何使用php email服务? 无论是用户注册、密码重置,还是订单确认,电子邮件都是与用户沟通的重要手段。AokSend将详细介绍如何实现php email功能,并提供一些配置技巧,帮助你更好…...
MapBox Android版开发 6 关于Logo
MapBox Android版开发 6 关于Logo Logo的显示查看源码及思路(Logo)第一步第二步 隐藏Logo示例查看源码及思路(Info)第一步第二步 隐藏Logo和Info示例 看到有网友留言问如何移除Logo,今天看了下V9源码,发现M…...
2024年房市
24年8月15日,国家统计局公布,“7月末,商品房待售面积73926万平方米”。(原文链接:https://www.stats.gov.cn/sj/zxfb/202408/t20240815_1955982.html) 7.39亿平方存量商品房,估价均价1万每平,总价约&am…...
index索引
index索引: create index 【1】on 【2】(【3】) 1为索引名,通常为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. 互联网服务提供商(ISP) 互联网服务提供商(ISP)是指提供互联网接入服务的公司或组织。它们负责将用户连接到互联网,并提供相关的服务,如电子邮件、网站托管和其他在线服务。ISP可以分为不同的层级&#…...
基于元神操作系统实现NTFS文件操作(三)
1. 背景 本文主要介绍DBR的读取和解析,并提供了基于元神操作系统的实现代码。由于解析DBR的目的是定位到NTFS磁盘分区的元文件$Root进行文件操作,所以只解析了少量的部分,其它部分可以参考相关文档进行理解。 DBR存在于磁盘分区的第一个扇区…...
深度学习与数学归纳法
最近发现,深度学习可以分为两个主要的阶段,分别是前向推理以及反向传播,分别对应着网络的推理和参数训练两个步骤。其中推理有时候也称为归纳推理。 在做参数训练的时候,本质上是在利用历史数据求网络参数的先验分布; …...
《Linux从小白到高手》理论篇(六):Linux软件安装一篇通
List item 本篇介绍Linux软件安装相关的操作命令,看完本文,有关Linux软件安装相关操作的常用命令你就掌握了99%了。 Linux软件安装 RPM RPM软件的安装、删除、更新只有root权限才能使用;查询功能任何用户都可以操作;如果普通用…...
【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 (接上一章) 自由协议通信步骤 (以MS-A2-1041为例) 接收与…...
matlab-对比两张图片的HSV分量的差值并形成直方图
%对比两张图片的HSV分量的差值并形成直方图,改个路径就能用,图片分辨率要一致 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解析部署使用全流程
官网地址: Spring Cloud Gateway 目录 1、SpringGateway简介 1、什么是网关 2、为什么用网关【为了转发】 2、应用: 1.启动nacos 2.创建网关项目 3.网关配置1 4.网关配置2【了解】 5.过滤器配置【了解】 1、SpringGateway简介 核心功能有三个&…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...
自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...
C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...
Linux基础开发工具——vim工具
文章目录 vim工具什么是vimvim的多模式和使用vim的基础模式vim的三种基础模式三种模式的初步了解 常用模式的详细讲解插入模式命令模式模式转化光标的移动文本的编辑 底行模式替换模式视图模式总结 使用vim的小技巧vim的配置(了解) vim工具 本文章仍然是继续讲解Linux系统下的…...
