hutool工具类 流接口 文件下载 文件传输 HttpServletResponse response 返回流 java.net.SocketException: Connection reset

 原因:url方式下载生产环境服务器部分图片时会中途链接断掉【bis.read(buffer)】【java.net.SocketException: Connection reset】

解决办法:更换为【接口方式获取文件流】

                // 发网络请求【接口方式获取文件流】
                JSONObject json = new JSONObject();
                json.put("imageUrl", document.getFilePath());
                // 调用下载电子档案文件接口(hutool工具类)
                InputStream in = HttpRequest.post(downloadDocUrl)
                        .header(CoreConstant.TOKEN_HEADER, token) // token转发
                        .body(json.toString())
                        .timeout(10000) // 超时,毫秒
                        .execute().bodyStream();

完整代码【接收端】

    /**
     * 网络文件下载-ZIP文件压缩【仅头像】
     *
     * @throws IOException
     */
    public static void downloadFolderOnlyPhoto(List<Document> documents, String dirName, String downloadDocUrl, HttpServletResponse response) throws IOException{
        // 获取token
        String token = null;
        // 获取request
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = requestAttributes.getRequest();
            if (!ObjectUtils.isEmpty(request)) {
                token = request.getHeader(CoreConstant.TOKEN_HEADER);
            }
        }

        try{
            ZipOutputStream out = new ZipOutputStream(response.getOutputStream());
            for (Document document : documents) {
                String virtualPath = document.getIdCard() + document.getFilePath().substring(document.getFilePath().lastIndexOf("."));
                ZipEntry zipEntry = new ZipEntry((!StringUtils.isEmpty(dirName) ? dirName + "/" : "")
                        + document.getStudentName() + document.getIdCard() + "/" + virtualPath);
                out.putNextEntry(zipEntry);
                // ################################################################################ //
                // 发网络请求【接口方式获取文件流】
                JSONObject json = new JSONObject();
                json.put("imageUrl", document.getFilePath());
                // 调用下载电子档案文件接口(hutool工具类)
                InputStream in = HttpRequest.post(downloadDocUrl)
                        .header(CoreConstant.TOKEN_HEADER, token) // token转发
                        .body(json.toString())
                        .timeout(10000) // 超时,毫秒
                        .execute().bodyStream();
                // like12 find bug,20250517,总是有bug,url方式下载生产环境服务器部分图片时会中途链接断掉【bis.read(buffer)】【java.net.SocketException: Connection reset】
                // InputStream in = HttpRequest.get(document.getFilePath()).execute().bodyStream(); // hutool工具也是一样的不行
                // InputStream in = new URL(document.getFilePath()).openConnection().getInputStream(); // 有bug,生产环境服务器部分图片url下载链接中途断掉
                // InputStream in = new URL(document.getFilePath()).openStream(); // 有bug,生产环境服务器部分图片url下载链接中途断掉
                BufferedInputStream bis = new BufferedInputStream(in);
                byte[] buffer = new byte[8192];
                int len;
                while ((len = bis.read(buffer)) != -1){
                    // System.out.println(document.getFilePath() + " len=" + len);
                    out.write(buffer,0, len);
                }
                // ################################################################################ //
                out.closeEntry();
                bis.close();
                in.close();
            }
            out.flush();
            out.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

完整代码【发送端】

   /**
     * 下载电子档案文件
     *
     * @param response
     * @param dto
     * @return
     */
    @Override
    public Result<?> downloadDoc(HttpServletResponse response, DownloadDocDto dto) {
        if(StringUtils.isEmpty(dto.getImageUrl())){
            return Result.fail("图片地址不能为空");
        }

        try {
            ServletOutputStream out = response.getOutputStream();
            InputStream in = new FileInputStream(dto.getImageUrl().replace(uploadUrlPre, uploadDir));
            BufferedInputStream bis = new BufferedInputStream(in);
            byte[] buffer = new byte[8192];
            int len;
            while ((len = bis.read(buffer)) != -1){
                out.write(buffer,0, len);
            }
            bis.close();
            in.close();
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
            return Result.fail("下载电子档案文件失败:" + e.getMessage());
        }

        return Result.success(dto.getImageUrl());
    }

<think>好的,我现在需要帮助用户了解如何使用Hutool工具实现多文件下载并生成压缩包,同时以文件流形式回。首先,我得回顾一下用户提供的引用内容,确保我正确理解他们的需求。 用户提到了引用[1]和[2],其中引用[1]提到使用Hutool生成多个Excel文件并合并成ZIP下载,而引用[2]则涉及Hutool的ZIP压缩包下载和临时目录的位置。用户的问题是如何使用Hutool实现多文件下载,打包成压缩包,并以文件回。因此,我需要结合这两个引用中的信息,提供一个清晰的步骤说明。 首先,我需要确定Hutool的相关模块。Hutool的Excel处理主要使用`cn.hutool.poi.excel`包,而ZIP处理则涉及`cn.hutool.core.util.ZipUtil`。同时,文件回可能需要用到Web框架,比如Spring MVC的`ResponseEntity`或者Servlet的`HttpServletResponse`。 接下来,步骤可能包括: 1. 生成多个Excel文件:每个文件可能对应不同的数据,需要循环创建。 2. 将这些文件打包成ZIP:使用ZipUtil将生成的Excel文件添加到压缩包中。 3.文件流形式回:将ZIP文件写入响应流,设置正确的HTTP头部,如Content-Type和Content-Disposition。 需要确保临时文件的处理,比如生成后及时删除,避免占用空间。引用[2]提到临时目录的位置,但用户可能需要将文件保存在内存中,或者使用临时文件后再清理。如果使用`ZipUtil.toZip()`方法,Hutool可能会自动处理临时文件,但需要确认是否支持内存中的操作,或者是否需要手动处理。 另外,需要考虑文件流的传输方式。在Spring Boot中,可以使用`ResponseEntity<Resource>`来文件流,而原生的Servlet则需要通过`HttpServletResponse`的输出流写入。 可能的代码结构: - 生成Excel部分:使用ExcelWriter,写入数据,输出到File或OutputStream。 - 打包ZIP:使用ZipUtil将多个File对象打包,或者直接将输出流写入ZIP。 - 文件流:设置响应头,将ZIP文件的内容写入响应输出流。 需要注意的是,如果文件较大,使用内存中的流可能会导致性能问题,因此可能需要使用临时文件,并在传输完成后删除它们。Hutool的ZipUtil是否有这样的机制?或者是否需要手动管理? 根据引用[1]中的描述,之前的项目用Apache POI,而Hutool更简便。所以,Hutool的API可能更简洁,比如直接提供将多个文件打包成ZIP的方法,并支持输出到流。 可能的步骤分解: 1. 创建多个Excel文件,保存到临时目录或内存中。 2. 使用ZipUtil将这些文件打包成一个ZIP文件,可能生成在临时目录中。 3. 读取ZIP文件作为输入流,并写入HTTP响应流。 4. 清理临时文件。 现在需要验证Hutool的具体方法。例如,`ZipUtil.toZip()`方法是否允许直接将多个文件打包到输出流,而不需要先保存到磁盘?或者是否需要先将每个Excel保存为临时文件,然后添加到ZIP中? 查阅Hutool的文档,`ZipUtil.toZip()`方法有一个重载,可以接受一个包含文件路径和别名的Map,以及一个输出流。例如: ```java ZipUtil.toZip(FileUtil.file("d:/temp/"), "test.zip", true); ``` 但若需要动态生成多个Excel文件并直接写入ZIP,可能需要使用`ZipOutputStream`。例如,每个Excel文件可以写入到ZipOutputStream的条目中。 或者,使用`ZipUtil.getZipOutputStream`来获取ZipOutputStream,然后逐个添加文件条目。这可能更灵活,尤其是在生成文件内容动态时。 例如,生成Excel文件到内存(如ByteArrayOutputStream),然后将其作为Zip条目添加。 这样的话,步骤可能调整为: 1. 对于每个需要生成的文件: a. 创建ExcelWriter,写入数据。 b. 将Excel内容写入ByteArrayOutputStream。 c. 将字节数组作为Zip条目添加到ZipOutputStream。 2. 将最终的ZipOutputStream转换为字节数组,写入响应流。 这种方法避免了临时文件的产生,完全在内存中处理,适合文件不大的情况。 但需要注意内存消耗,如果文件很大,可能会导致内存溢出,这时还是需要临时文件。 所以,可能需要根据文件大小选择不同的策略。用户的需求中可能是一般大小的文件,所以内存处理可行。 现在,结合Hutool的API,如何实现? 首先,生成Excel到输出流: 使用ExcelWriter的flush方法,将内容写入输出流。例如: ```java ExcelWriter writer = ExcelUtil.getWriter(); // ... 写入数据 ByteArrayOutputStream out = new ByteArrayOutputStream(); writer.flush(out, true); writer.close(); byte[] excelBytes = out.toByteArray(); ``` 然后将这个字节数组作为Zip条目添加。 使用ZipUtil的ZipOutputStream: ```java ByteArrayOutputStream zipOut = new ByteArrayOutputStream(); ZipOutputStream zip = ZipUtil.getZipOutputStream(zipOut); zip.putNextEntry(new ZipEntry("file1.xlsx")); zip.write(excelBytes1); zip.closeEntry(); // 同样处理其他文件... zip.finish(); byte[] zipBytes = zipOut.toByteArray(); ``` 然后将zipBytes写入HttpServletResponse的输出流。 在Spring Boot中,可以ResponseEntity: ```java return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"download.zip\"") .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(new ByteArrayResource(zipBytes)); ``` 或者使用HttpServletResponse: ```java response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=download.zip"); ServletOutputStream sos = response.getOutputStream(); sos.write(zipBytes); sos.flush(); ``` 这样整个流程不需要临时文件,完全在内存中处理。 但是,如果文件很大,内存可能不够,这时候还是需要临时文件。例如,生成每个Excel到临时文件,然后使用ZipUtil将它们打包成一个ZIP文件,再以文件回。 这种情况下,代码可能如下: 生成Excel文件到临时目录: ```java File excelFile1 = FileUtil.createTempFile("temp-", ".xlsx", true); ExcelWriter writer1 = ExcelUtil.getWriter(excelFile1); // 写入数据 writer1.flush(); writer1.close(); ``` 重复生成多个文件后,使用ZipUtil打包: ```java File zipFile = ZipUtil.zip(FileUtil.file("tempDir"), "download.zip", true); ``` 然后读取zipFile为输入流,并写入响应: ```java FileInputStream fis = new FileInputStream(zipFile); IOUtils.copy(fis, response.getOutputStream()); fis.close(); // 删除临时文件和zipFile FileUtil.del(tempDir); ``` 这样处理的话,需要注意临时目录的清理,尤其是在异常情况下,确保不会残留文件。 总结,用户的需求可能需要根据文件大小选择内存处理或临时文件处理。Hutool的ZipUtil和ExcelUtil提供了相应的方法来简化这些操作。现在需要将这些步骤整理成一个清晰的步骤说明,并提供代码示例。 此外,用户希望以文件流形式回,所以需要确保在响应中正确设置头信息,使得浏览器能识别为下载文件,并正确处理文件名。 最后,需要生成相关问题,可能涉及性能优化、大文件处理、异常处理等。</think>### Hutool文件下载压缩包实现步骤 #### 1. 添加Hutool依赖 ```xml <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.16</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency> ``` #### 2. 动态生成Excel文件 使用`ExcelUtil`生成多个Excel到内存或临时目录: ```java // 生成第一个Excel文件(内存操作) ExcelWriter writer1 = ExcelUtil.getWriter(); writer1.writeCellValue(0, 0, "数据1"); ByteArrayOutputStream excelStream1 = new ByteArrayOutputStream(); writer1.flush(excelStream1, true); writer1.close(); // 生成第二个Excel文件(临时文件) File excelFile2 = FileUtil.createTempFile("temp-", ".xlsx", true); ExcelWriter writer2 = ExcelUtil.getWriter(excelFile2); writer2.writeCellValue(0, 0, "数据2"); writer2.close(); ``` #### 3. 打包为ZIP文件流 **方案1:完全内存操作(适合小文件)** ```java ByteArrayOutputStream zipStream = new ByteArrayOutputStream(); ZipOutputStream zip = ZipUtil.getZipOutputStream(zipStream); // 添加内存中的Excel文件 zip.putNextEntry(new ZipEntry("file1.xlsx")); zip.write(excelStream1.toByteArray()); zip.closeEntry(); // 添加临时文件 zip.putNextEntry(new ZipEntry("file2.xlsx")); zip.write(FileUtil.readBytes(excelFile2)); zip.closeEntry(); zip.finish(); byte[] zipBytes = zipStream.toByteArray(); ``` **方案2:临时文件操作(适合大文件)** ```java File zipFile = FileUtil.createTempFile("download-", ".zip", true); ZipUtil.zip(zipFile, false) .add(FileUtil.file("file1.xlsx"), excelStream1.toByteArray()) .add(excelFile2) .close(); ``` #### 4. 文件流(Spring Boot示例) ```java @GetMapping("/download") public ResponseEntity<Resource> download() throws IOException { // 获取ZIP字节数组或文件 byte[] zipBytes = ...; // 来自方案1 // File zipFile = ...; // 来自方案2 // 设置响应头 return ResponseEntity.ok() .header("Content-Disposition", "attachment; filename=download.zip") .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(new ByteArrayResource(zipBytes)); // 或使用FileSystemResource(zipFile) } ``` #### 5. 清理临时文件(方案2需手动清理) ```java FileUtil.del(excelFile2); // 删除单个文件 FileUtil.del(zipFile); // 删除压缩包 ``` ### 关键API说明 - `ExcelUtil.getWriter()`:创建内存Excel写入对象 - `ZipUtil.getZipOutputStream()`:获取ZIP输出流 - `FileUtil.createTempFile()`:创建自动删除的临时文件[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值