多sheet导出并批量下载附件

  • 前言
  • 源码
  • 代码实现
    • Controller
    • 接口类
    • 实现类 (重中之重)
      • 实现类重点强调
  • 测试
  • 结语

前言

最近浏览语雀社区官网,里面有一篇 easyexcel 里面 图片导出,对此,我产生了浓厚兴趣。恰巧公司有这么一个BT 需求,但是作为有追求的人,图片放在 excel 单元格是不是太不美观了,万一图片很大,那不是奇丑无比了! 有没有什么办法让图片下载到指定的本地文件路径,这样excel 既美观,又不影响图片查看。

上一次写了一篇: EasyExcel3.0.5导出多个sheet,含查询优化,竟然无人问津,这篇写的非常完整,既实现了多个 sheet 导出,也实现了附件下载。但是批量下载附件有几个问题点

  • 附件地址解析的局限性。我是存储在 OSS,OSS 返回的路径可以用流直接下载,但是如果有人不用OSS 呢,五花八门的附件 URL 防不胜防,就会导致解析出错!例如遇到下面的 URL 地址,该怎么玩?现实中就有!
https://label1.cmscentertech.com/Label/Print?coNumber=NC2022040700374618&type=MainLabel&childForecastNumber=YT2203721272081883-1&labelformat=PDFhttp://yunexpress-fileupload.oss-cn-shenzhen.aliyuncs.com/eddfbbe9c142456f9984d706e10a01e4.Jpeg
  • 保存附件地址的问题。创建一个本地文件目录,貌似好像没啥毛病,但是上线部署到服务器的时候,却不能下载到本地了,因为服务器上没有 D盘、C盘,而且只会在服务器创建个目录,egg pain 吗?如下图,服务器上产生了目录并没有下载到你的本地目录。

好了,遇到问题解决问题,才能帮助自己成长。话不多说,开干!

源码

链接:https://pan.baidu.com/s/1q7s6JisKKJ-F_ffN6Uifhg?pwd=1234
提取码:1234

代码实现

为了让读者可以轻松读懂读通,并可以参照实现,还是贴完整一点。

Controller

package cn.com.easyExcel.controller;import cn.com.easyExcel.param.OrderExportParam;
import cn.com.easyExcel.service.OrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Api(tags = "订单表")
@RestController
@RequestMapping(value = "/order")
public class OrderController {@Autowiredprivate OrderService orderService;@ApiOperation(value = "多sheet导出并下载附件")@PostMapping(value = "moreSheetAndDownloadUrlFile")public void moreSheetAndDownloadUrlFile(HttpServletRequest request,HttpServletResponse response,OrderExportParam param){orderService.moreSheetAndDownloadUrlFile(request, response, param);}
}

接口类

package cn.com.easyExcel.service;import cn.com.easyExcel.param.OrderExportParam;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public interface OrderService {/*** 多sheet导出并且下载附件*/void moreSheetAndDownloadUrlFile(HttpServletRequest request, HttpServletResponse response, OrderExportParam param);
}

实现类 (重中之重)

package cn.com.easyExcel.service.impl;import cn.com.easyExcel.enums.InvoiceTypeEnum;
import cn.com.easyExcel.enums.SourceTypeEnum;
import cn.com.easyExcel.excel.listener.ExportListener;
import cn.com.easyExcel.mapper.OrderDetailMapper;
import cn.com.easyExcel.mapper.OrderMapper;
import cn.com.easyExcel.param.OrderExportParam;
import cn.com.easyExcel.pojo.Order;
import cn.com.easyExcel.pojo.OrderDetail;
import cn.com.easyExcel.pojo.OrderImage;
import cn.com.easyExcel.service.OrderService;
import cn.com.easyExcel.util.InitDataUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;@Slf4j
@Service
public class OrderServiceImpl implements OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate OrderDetailMapper orderDetailMapper;private static final int PAGE_SIZE = 1000;private static final String PATTERN = "yyyy-MM-dd-HH-mm-ss";//需要压缩的文件夹private final static String DOWNLOAD_DIR = "download";//打包后的文件夹private final static String DOWNLOAD_ZIP = "downloadZip";DateTimeFormatter dtf = DateTimeFormatter.ofPattern(PATTERN);@SneakyThrows@Overridepublic void moreSheetAndDownloadUrlFile(HttpServletRequest request,HttpServletResponse response,OrderExportParam param) {long startTime = System.currentTimeMillis();// 1.定义变量List<Order> allOrderList = new ArrayList<>();   //订单信息List<OrderDetail> allOrderDetailList = new ArrayList<>();   //订单详情信息List<OrderImage> allImageList = new ArrayList<>();    //订单图片信息List<String> allImageFileList = new ArrayList<>();    //订单图片文件// 2.获取数据LambdaQueryWrapper<Order> queryWrapper = Wrappers.lambdaQuery();//TODO: 组装查询(省略)int startIndex = 1;while (true){int startParam =(startIndex - 1) * PAGE_SIZE;int pageIndex = (int) Math.ceil((double) startParam / (double) PAGE_SIZE+1);Page<Order> pageQuery = new Page<>(pageIndex, PAGE_SIZE, false);Page<Order> orderListByPage = orderMapper.selectPage(pageQuery, queryWrapper);//订单信息List<Order> orderList = orderListByPage.getRecords();if (CollectionUtils.isEmpty(orderList)) {break;}allOrderList.addAll(orderList);//订单详情LambdaQueryWrapper<OrderDetail> detailWrapper = Wrappers.lambdaQuery();List<Long> orderIds = orderList.stream().map(Order::getOrderId).collect(Collectors.toList());detailWrapper.in(OrderDetail::getOrderId, orderIds);List<OrderDetail> orderDetailList = orderDetailMapper.selectList(detailWrapper);    //查询详情allOrderDetailList.addAll(orderDetailList);//订单图片List<String> imageFileList = orderDetailList.stream().map(OrderDetail::getImage).collect(Collectors.toList());allImageFileList.addAll(imageFileList);List<OrderImage> imageList = new ArrayList<>();for (String image : imageFileList) {OrderImage orderImage = OrderImage.builder().image(image).build();imageList.add(orderImage);}allImageList.addAll(imageList);startIndex++;}// 3. easyExcel组件String nowTime = dtf.format(LocalDateTime.now().plusHours(8));ExcelWriter excelWriter = null;if(CollUtil.isNotEmpty(allImageFileList)){File fileMkDir = new File(DOWNLOAD_DIR);if(!fileMkDir.exists()){fileMkDir.mkdir();}String filePath = DOWNLOAD_DIR + "/" + "Order" + nowTime + ".xlsx";File file = new File(filePath);excelWriter = EasyExcel.write(file).build();} else {ServletOutputStream outputStream = ExportListener.getServletOutputStream(response, "订单信息");excelWriter = EasyExcel.write(outputStream).build();}if (CollectionUtils.isNotEmpty(allOrderList)) {WriteSheet orderSheet = EasyExcel.writerSheet(0, "订单信息").head(Order.class).build();excelWriter.write(allOrderList, orderSheet);}if (CollectionUtils.isNotEmpty(allOrderDetailList)) {WriteSheet detailSheet = EasyExcel.writerSheet(1, "订单详情").head(OrderDetail.class).build();excelWriter.write(allOrderDetailList, detailSheet);}if (CollectionUtils.isNotEmpty(allImageList)) {WriteSheet imageSheet = EasyExcel.writerSheet(2, "订单图片").head(OrderImage.class).build();excelWriter.write(allImageList, imageSheet);}// 千万别忘记finish 会帮忙关闭流if (excelWriter != null) {excelWriter.finish();}// 4.下载附件File fileDir = null;if(CollUtil.isNotEmpty(allImageFileList)){for(String imageFile : allImageFileList){String fileName = nowTime + (int)((Math.random()*9+1)* 100000) + ".pdf";fileDir = URLDownLoad(imageFile, fileName);}downloadZip(request, response, nowTime);}//删除暂存目录if(null != fileDir){FileUtils.deleteDirectory(fileDir);}long endTime = System.currentTimeMillis();log.info("多sheet导出耗时:{}", endTime - startTime);}public File URLDownLoad(String urlStr, String fileName) throws IOException {//1. 定义URLConnectionURL url = new URL(urlStr);URLConnection conn = url.openConnection();conn.setConnectTimeout(3 * 1000);   // 设置超时间为3秒conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36");InputStream inputStream = null;try {inputStream = conn.getInputStream();} catch (IOException e) {log.error("文件资源不存在,下载失败,错误信息:{}", e.getMessage());return new File(DOWNLOAD_DIR);}//2. 获取文件名String headerField = conn.getHeaderField("Content-Disposition");String realFileName = null;if(StrUtil.isNotEmpty(headerField)){realFileName = URLDecoder.decode(headerField.split(";")[2].split("=")[1], "UTF-8");   //ossUrl 头部解析的文件名}String urlFile = conn.getURL().getFile();   //url 路径的文件名String realFile = StrUtil.isNotEmpty(realFileName) ? realFileName : urlFile;if (realFile != null && realFile.indexOf(".") > 0) {    //如果文件名没有后缀,用随机生成的pdf后缀fileName = Optional.of(realFile).orElse(fileName);}//3. 创建临时文件File fileDir = new File(DOWNLOAD_DIR);if(!fileDir.exists()){fileDir.mkdir();}String filePath = DOWNLOAD_DIR + "/" + fileName;File file = new File(filePath);file.createNewFile();//创建文件,存在覆盖FileOutputStream outputStream = new FileOutputStream(file);byte[] buffer = new byte[1024 * 5];int len = 0;while ((len = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, len);}outputStream.close();inputStream.close();return fileDir;}/*** 下载压缩包*/public void downloadZip(HttpServletRequest request,HttpServletResponse response, String nowTime) throws IOException {OutputStream out = null;File zip = null;File fileDir = new File(DOWNLOAD_ZIP);if(!fileDir.exists()){//如果文件夹不存在fileDir.mkdir();//创建文件夹}//多个文件进行压缩,批量打包下载文件//创建压缩文件需要的空的zip包String zipName = "order-".concat(nowTime).concat(".zip");String zipFilePath = DOWNLOAD_ZIP + File.separator + zipName;//压缩文件zip = new File(zipFilePath);zip.createNewFile();//创建文件,存在覆盖//创建zip文件输出流ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zip));this.zipFile(DOWNLOAD_DIR, zos);zos.close();//将打包后的文件写到客户端,输出的方法同上,使用缓冲流输出BufferedInputStream bis = new BufferedInputStream(new FileInputStream(zipFilePath));byte[] buff = new byte[bis.available()];bis.read(buff);bis.close();//IO流实现下载的功能response.setCharacterEncoding("UTF-8"); //设置编码字符response.setContentType("application/zip");//防止文件名乱码String userAgent = request.getHeader("USER-AGENT");if (userAgent.contains("Firefox") || userAgent.contains("firefox")) {//火狐浏览器zipName = new String(zipName.getBytes(), "ISO8859-1");} else {zipName = URLEncoder.encode(zipName, "UTF-8");//其他浏览器}response.setHeader("Content-Disposition", "attachment;filename=" + zipName);//设置下载的压缩文件名称out = response.getOutputStream();   //创建页面返回方式为输出流,会自动弹出下载框out.write(buff);//输出数据文件//下载完后删除文件夹和压缩包deleteFile(fileDir);}/*** 删除文件及文件夹下所有文件*/public void deleteFile(File file) {//判断文件不为null或文件目录存在//获取目录下子文件File[] files = file.listFiles();//遍历该目录下的文件对象for (File f : files) {//判断子目录是否存在子目录,如果是文件则删除if (f.isDirectory()) {//递归删除目录下的文件deleteFile(f);} else {//文件删除f.delete();}}//文件夹删除file.delete();}/*** 压缩文件* @param filePath  需要压缩的文件夹* @param zos   zip文件输出流*/private void zipFile(String filePath,ZipOutputStream zos) throws IOException {File inputFile = new File(filePath);  //根据文件路径创建文件if(inputFile.exists()) { //判断文件是否存在if (inputFile.isFile()) {  //判断是否属于文件,还是文件夹//创建输入流读取文件BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputFile));//将文件写入zip内,即将文件进行打包zos.putNextEntry(new ZipEntry(inputFile.getName()));//写入文件的方法,同上int size = 0;byte[] buffer = new byte[1024];  //设置读取数据缓存大小while ((size = bis.read(buffer)) > 0) {zos.write(buffer, 0, size);}bis.close();} else {  //如果是文件夹,写入zipFile[] files = inputFile.listFiles();if (files != null) {for (File fileTem : files) {zipFile(fileTem.toString(),zos);}}}}//关闭输入输出流zos.closeEntry();}}

实现类重点强调

声明:

  1. 实现类这里可以拆分几个方法,把每个步骤做的事情分开;
  2. 硬编码的部分,可以定常量,时间问题,此处省略了。

代码核心:
分析过程只截取部分重点。

多sheet导出的核心部分:
a. 要实现多个 sheet 首先声明多个集合接收各自的数据

List<Order> allOrderList = new ArrayList<>();   //订单信息List<OrderDetail> allOrderDetailList = new ArrayList<>();   //订单详情信息List<OrderImage> allImageList = new ArrayList<>();    //订单图片信息

b. 根据不同的对象分别 ExcelWriter

if (CollectionUtils.isNotEmpty(allOrderList)) {WriteSheet orderSheet = EasyExcel.writerSheet(0, "订单信息").head(Order.class).build();excelWriter.write(allOrderList, orderSheet);
}
if (CollectionUtils.isNotEmpty(allOrderDetailList)) {WriteSheet detailSheet = EasyExcel.writerSheet(1, "订单详情").head(OrderDetail.class).build();excelWriter.write(allOrderDetailList, detailSheet);
}
if (CollectionUtils.isNotEmpty(allImageList)) {WriteSheet imageSheet = EasyExcel.writerSheet(2, "订单图片").head(OrderImage.class).build();excelWriter.write(allImageList, imageSheet);
}

c. 生成压缩包的时候,要清楚历史文件,不然会累加堆积,deleteFile 删除历史文件,当再一次点击下载时,就不会遗留上一次的文件。

public void deleteFile(File file) {//判断文件不为null或文件目录存在//获取目录下子文件File[] files = file.listFiles();//遍历该目录下的文件对象for (File f : files) {//判断子目录是否存在子目录,如果是文件则删除if (f.isDirectory()) {//递归删除目录下的文件deleteFile(f);} else {//文件删除f.delete();}}//文件夹删除file.delete();}

测试


结语

完整的项目代码结果,请移步查看 EasyExcel3.0.5导出多个sheet,含查询优化,内容太多,细节的代码不贴了,先把项目搭起来。 把上一篇的附件下载的实现替换成本篇的实现,就可以了。 代码发布到服务器依然可以下载到自己本地,同时多种多样的附件路径都可以下载解析。 好了,欢迎仁人志士留言评论。

EasyExcel3.0.5 导出多个sheet,批量下载打包成ZIP压缩包相关推荐

  1. 多文件批量下载打包成.zip

    多文件批量下载打包成.zip //下载文件打包成zip压缩包@GetMapping("/zip/{id}")public void zip(@PathVariable(" ...

  2. java文件批量下载打包成zip

    1.首先html页面获取当前需要下载文件的id集合将转成字符串形式传递到后台. ................ 2 .下面就是获取字符串之后的批量下载/*** 多文件批量下载压缩成zip**root ...

  3. Java实现批量下载(打包成zip)

    批量下载 引言 一.前端JS发送下载请求 1.ajax无法发送下载请求 2.js发送下载请求:超链接方式 3.js发送下载请求:拼接Form表单,并提交 二.后台处理 三.结束 引言 批量下载思路,首 ...

  4. Java实现文件批量下载,打包成zip压缩包

       最近在做一个管理系统的项目,需要实现一个功能,就是批量下载文件,并打包成zip压缩包.    前端通过POST请求传来要下载的文件列表,Java代码实现如下: import java.io.Bu ...

  5. java 导出表格打包zip文件下载_asyExcel导出excel并打包成zip压缩包下载

    假期期间自己在家撸码,刚好用到了导出,导出来之后是多个文件,所以需要打成压缩包并下载来给客户.查阅了一些资料,把这段代码贴在这,相当于有个记录吧. package com.business.testE ...

  6. easyExcel导出excel文件并打包成zip压缩包下载

    文件导出 专栏收录该内容 2 篇文章0 订阅 订阅专栏 package com.business.testExcelPort; import java.io.BufferedInputStream; ...

  7. 批量下载文件,打包成zip压缩包

    批量下载文件,用程序打成zip压缩包在下载 前台传来要下载的url数组 @RequestMapping(value = "/download",method = RequestMe ...

  8. Java实现批量下载图片,打包成zip压缩包

    /*** 批量下载图片后台逻辑* @return*/ @GetMapping("/downLoad") public void download(HttpServletReques ...

  9. java 批量下载图片并打包成zip压缩包

    项目需求: 对接第三方接口时,因为数据中存储的是图片链接,第三方审核时需要把所有图片以zip压缩包的方式传入到他们ftp服务中. 提示:以下是本篇文章正文内容,下面案例可供参考 话不多说直接看代码,案 ...

最新文章

  1. locate和find命令
  2. 剑指offer:表示数值的字符串cpp题解:指针
  3. C# FileSystemWatcher文件监控实例
  4. OpenCV学习笔记(十二):边缘检测:Canny(),Sobel(),Laplace(),Scharr滤波器
  5. 如何让测试同学理解并发锁
  6. 【渝粤教育】国家开放大学2018年春季 0704-22T民法基础与实务 参考试题
  7. 一台计算查找另一台计算机,将SSH密钥从一台计算机移动到另一台计算机 | MOS86...
  8. Qt配置OpenCV教程
  9. LBP特征提取原理和python库代码
  10. 在浏览器上播放虚幻引擎:像素流前端教程
  11. php7isapi,WINDOWS 2000下使用ISAPI方式安装PHP
  12. 分析Kettle性能测试过程中的STARTDATE时间问题
  13. NPOI操作Excel 让单元格的内容换行
  14. 头条号发视频有收益吗,头条号发视频就有收益吗
  15. Photoshop学习(十三):利用图层制作凹陷效果
  16. 从汇编角度看一个C函数调用
  17. JAVA容器,什么叫JAVA容器?
  18. dCAM: Dimension-wise Class Activation Mapfor Explaining Multivariate Data Series Classification(DB)
  19. 文思海辉与雅观科技达成战略合作,加速智慧场景应用落地
  20. 垃圾的LinkedIn(领英)网站

热门文章

  1. Python中“xx+=xx”与“xx=xx+xx”的区别
  2. VR和云服务,使出洪荒之力也难擦出火花
  3. 网站怎么备案?如何快速通过网站备案?网站快速备案攻略请查收!
  4. 2015阿里系统工程师面试经验分享(广州站)
  5. nafxcw.lib(dllmodul.obj) : error LNK2005: _DllMain@12 already defined问题解决
  6. 2015年SaaS细分领域部分代表公司盘点 融资额近40亿
  7. 编译报错unable to initialize decompress status for section .debug_info
  8. 2022危险化学品生产单位主要负责人考试题库及模拟考试
  9. python程序设计心得体会感想-从Python学习中得到的一点感悟
  10. macOS Monterey 12.5 (21G72) 正式版 ISO、IPSW、PKG 下载