Java下载多个文件打成压缩包返回输出流,并解决被JVM占用无法打开

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,恶意文件检测 1000次 1年
对象存储 OSS,内容安全 1000 次 1年
简介: Java下载多个文件打成压缩包返回输出流,并解决被JVM占用无法打开

需求


给前端提供一个接口,可下载文件,如果只是一个文件就直接返回输出流,如果多个就打成一个压缩包返回输出流


参考代码


代码根据自己的业务逻辑进行修改!


controller

```java
/**
   * 打包下载文档
   * @param signId
   */
  @PostMapping("/downloadZip")
  @ApiOperationSupport(order = 5)
  @ApiOperation(value = "下载文档", notes = "传入signId")
  public R download(@ApiParam(value = "签署id", required = true) @RequestParam Long signId) throws Exception {
    return R.data(signDocumentService.downloadZipFile(signId).toString());
  }
```

service

/**
   * 根据id查询签署附件打包并下载
   *
   * @param signId
   * @return FileOutputStream
   * @throws Exception
   */
  FileOutputStream downloadZipFile(Long signId) throws Exception;

serviceImpl

 @Override
    public FileOutputStream downloadZipFile(Long signId) throws Exception {
        InputStream input = null;
        //  定义压缩输出流
        ZipOutputStream zipOut = null;
        File zipFile = null;
        List<String> ossList = null;
        String path = null;
        String download = null;
        String localPath = null;
        try {
            ossList = new ArrayList();
            // 根据signId查询签署文档
            List<SignDocumentVO> signDocuments = signDocumentMapper.selectAttachs(signId);
            // 如果不为空
            if (!signDocuments.isEmpty()) {
                // 如果只有一个签署文件就直接下载,如果有多个就打压缩包
                if (signDocuments.size()==FILE_ONE.getCode()) {
                    return downloadDetails(signId);
                }else{
                    for (SignDocument signDocument : signDocuments) {
                        // 根据签署文档的attachId查询对应的附件
                        Attach attach = attachMapper.selectAttachById(signDocument.getAttachId());
                        String link = attach.getLink();
                        // 把oss文件下载到项目本地
                        path = SignDocumentServiceImpl.class.getClass().getResource("/").getPath() +
                                FileUtil.getNameWithoutExtension(link) + "." + PDF;
                        localPath = SignDocumentServiceImpl.class.getClass().getResource("/").getPath();
                        // 把获取到的oss文件链接下载到本地
                        download = download(link, path);
                        ossList.add(download);
                        //  定义压缩文件夹的名称和相关的位置
                        zipFile = new File(localPath + "file.zip");
                        //  实例化压缩输出流  并定制压缩文件的输出路径
                        zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
                    }
                }
            }
            for (String o : ossList) {
                File file = new File(o);
                //  定义输入文件流
                input = new FileInputStream(file);
                // 一个文件对对应一个ZipEntry实体
                zipOut.putNextEntry(new ZipEntry(file.getName()));
                int readsize = 1024 * 1024 * 5;
                byte[] buf = new byte[readsize];
                int temp = 0;
                while ((temp = input.read(buf)) != -1) {
                    zipOut.write(buf, 0, temp);
                }
            }
            //  删除本地文件
           // FileUtil.deleteQuietly(new File(path));
        } catch (IOException e) {
            log.error("打包下载失败!", e);
        } finally {
            if(null != zipOut){
                zipOut.closeEntry();
                zipOut.close();
            }
            if(null != input){
                input.close();
            }
            // 回收资源
            System.gc();
        }
        // 压缩包文件转为FileOutputStream
        FileOutputStream fileOutputStream = new FileOutputStream(String.valueOf(zipOut));
        return fileOutputStream;
    }
    private FileOutputStream downloadDetails(Long signId) throws Exception {
        String path = null;
        String download = null;
        FileOutputStream fileOutputStream = null;
        // 根据signId查询签署文档
        List<SignDocumentVO> signDocuments = signDocumentMapper.selectAttachs(signId);
        // 如果不为空
        if (!signDocuments.isEmpty()) {
            for (SignDocument signDocument : signDocuments) {
                // 根据签署文档的attachId查询对应的附件
                Attach attach = attachMapper.selectAttachById(signDocument.getAttachId());
                String link = attach.getLink();
                // 把oss文件下载到项目本地
                path = SignDocumentServiceImpl.class.getClass().getResource("/").getPath() +
                FileUtil.getNameWithoutExtension(link) + "." + PDF;
                // 把获取到的oss文件链接下载到本地
                download = download(link, path);
                File file = new File(download);
                fileOutputStream = new FileOutputStream(file);
            }
        }
        return fileOutputStream;
    }

调试效果

image.png

image.png

解压缩之后

image.png

解决文件被JVM占用无法打开

image.png


排查代码发现,流都关了,还是会出现这个问题,只能强制使用gc了

image.png

相关实践学习
通义万相文本绘图与人像美化
本解决方案展示了如何利用自研的通义万相AIGC技术在Web服务中实现先进的图像生成。
目录
相关文章
|
2月前
|
安全 Oracle Java
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
214 0
JAVA高级开发必备·卓伊凡详细JDK、JRE、JVM与Java生态深度解析-形象比喻系统理解-优雅草卓伊凡
|
3月前
|
存储 运维 Kubernetes
Java启动参数JVM_OPTS="-Xms512m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError"
本文介绍了Java虚拟机(JVM)常用启动参数配置,包括设置初始堆内存(-Xms512m)、最大堆内存(-Xmx1024m)及内存溢出时生成堆转储文件(-XX:+HeapDumpOnOutOfMemoryError),用于性能调优与故障排查。
340 0
|
3月前
|
存储 Java 编译器
深入理解Java虚拟机--类文件结构
本内容介绍了Java虚拟机与Class文件的关系及其内部结构。Class文件是一种与语言无关的二进制格式,包含JVM指令集、符号表等信息。无论使用何种语言,只要能生成符合规范的Class文件,即可在JVM上运行。文章详细解析了Class文件的组成,包括魔数、版本号、常量池、访问标志、类索引、字段表、方法表和属性表等,并说明其在Java编译与运行过程中的作用。
|
3月前
|
存储 人工智能 Java
java之通过Http下载文件
本文介绍了使用Java实现通过文件链接下载文件到本地的方法,主要涉及URL、HttpURLConnection及输入输出流的操作。
209 0
|
4月前
|
存储 安全 算法
Java 集合面试题 PDF 下载及高频考点解析
本文围绕Java集合面试题展开,详细解析了集合框架的基本概念、常见集合类的特点与应用场景。内容涵盖`ArrayList`与`LinkedList`的区别、`HashSet`与`TreeSet`的对比、`HashMap`与`ConcurrentHashMap`的线程安全性分析等。通过技术方案与应用实例,帮助读者深入理解集合类的特性和使用场景,提升解决实际开发问题的能力。文末附带资源链接,供进一步学习参考。
109 4
|
5月前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
492 55
|
5月前
|
Arthas 存储 Java
JVM深入原理(三+四):JVM组成和JVM字节码文件
目录3. JVM组成3.1. 组成-运行时数据区3.2. 组成-类加载器3.3. 组成-执行引擎3.4. 组成-本地接口4. JVM字节码文件4.1. 字节码文件-组成4.1.1. 组成-基础信息4.1.1.1. 基础信息-魔数4.1.1.2. 基础信息-主副版本号4.1.2. 组成-常量池4.1.3. 组成-方法4.1.3.1. 方法-工作流程4.1.4. 组成-字段4.1.5. 组成-属性4.2. 字节码文件-查看工具4.2.1. javap4.2.2. jclasslib4.2.3. 阿里Arthas
97 0
|
5月前
|
存储 安全 Java
JVM深入原理(五):JVM组成和JVM字节码文件
类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析。
72 0
|
5月前
|
存储 监控 算法
Java程序员必学:JVM架构完全解读
Java 虚拟机(JVM)是 Java 编程的核心,深入理解其架构对开发者意义重大。本文详细解读 JVM 架构,涵盖类加载器子系统、运行时数据区等核心组件,剖析类加载机制,包括加载阶段、双亲委派模型等内容。阐述内存管理原理,介绍垃圾回收算法与常见回收器,并结合案例讲解调优策略。还分享 JVM 性能瓶颈识别与调优方法,分析 Java 语言特性对性能的影响,给出数据结构选择、I/O 操作及并发同步处理的优化技巧,同时探讨 JVM 安全模型与错误处理机制,助力开发者提升编程能力与程序性能。
Java程序员必学:JVM架构完全解读
|
6月前
|
Arthas 监控 Java
Arthas redefine(加载外部的.class文件,redefine到JVM里 )
Arthas redefine(加载外部的.class文件,redefine到JVM里 )
216 15