Spring REST - 创建.zip文件并将其发送到客户端

aza*_*lut 31 java rest zip spring zipoutputstream

我想创建包含我从后端接收的压缩文件的.zip文件,然后将此文件发送给用户.2天我一直在寻找答案,无法找到合适的解决方案,也许你可以帮助我:)

现在,代码是这样的:(我知道我不应该在spring控制器中完成所有操作,但不关心它,它只是为了测试目的,找到让它工作的方法)

    @RequestMapping(value = "/zip")
    public byte[] zipFiles(HttpServletResponse response) throws IOException{
        //setting headers
        response.setContentType("application/zip");
        response.setStatus(HttpServletResponse.SC_OK);
        response.addHeader("Content-Disposition", "attachment; filename=\"test.zip\"");

        //creating byteArray stream, make it bufforable and passing this buffor to ZipOutputStream
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream);
        ZipOutputStream zipOutputStream = new ZipOutputStream(bufferedOutputStream);

        //simple file list, just for tests
        ArrayList<File> files = new ArrayList<>(2);
        files.add(new File("README.md"));

        //packing files
        for (File file : files) {
            //new zip entry and copying inputstream with file to zipOutputStream, after all closing streams
            zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
            FileInputStream fileInputStream = new FileInputStream(file);

            IOUtils.copy(fileInputStream, zipOutputStream);

            fileInputStream.close();
            zipOutputStream.closeEntry();
        }

        if (zipOutputStream != null) {
            zipOutputStream.finish();
            zipOutputStream.flush();
            IOUtils.closeQuietly(zipOutputStream);
        }
        IOUtils.closeQuietly(bufferedOutputStream);
        IOUtils.closeQuietly(byteArrayOutputStream);
        return byteArrayOutputStream.toByteArray();
    }
Run Code Online (Sandbox Code Playgroud)

但问题是,使用代码,当我输入URL:localhost:8080/zip我得到文件:test.zip.html而不是.zip文件

当我删除.html扩展名并只留下test.zip时,它会正确打开如何避免返回此.html扩展名?为什么要添加?

我不知道我还能做什么.我也尝试用以下方法替换ByteArrayOuputStream:

OutputStream outputStream = response.getOutputStream();
Run Code Online (Sandbox Code Playgroud)

并将方法设置为void所以它什么都不返回,但是它创建了.zip文件,它被损坏了?

压缩test.zip之后在我的macbook上我得到了test.zip.cpgz,它再次给了我test.zip文件等等.

在Windows上,.zip文件被破坏,正如我所说,甚至无法打开它.

我也认为,自动删除.html扩展名将是最佳选择,但如何?希望它没有像看起来那么难:)谢谢

aza*_*lut 28

似乎已经解决了.我换了:

response.setContentType("application/zip");
Run Code Online (Sandbox Code Playgroud)

有:

@RequestMapping(value = "/zip", produces="application/zip")
Run Code Online (Sandbox Code Playgroud)

现在我变得清晰,漂亮.zip文件:)

如果你们中的任何人有更好或更快的主张,或者只是想提出一些建议,那么请继续,我很好奇.

  • +1 对于重要的问题并回答您自己的问题!我对此实现的唯一问题/担忧是最终的返回语句。我认为这会将整个文件加载到内存中,因为“toByteArray()”。正确的?我想知道是否有一种方法可以对其进行流式传输,以避免在目录太大时出现一些 OutOfMemoryException 错误。 (3认同)

den*_*nov 25

@RequestMapping(value="/zip", produces="application/zip")
public void zipFiles(HttpServletResponse response) throws IOException {

    //setting headers  
    response.setStatus(HttpServletResponse.SC_OK);
    response.addHeader("Content-Disposition", "attachment; filename=\"test.zip\"");

    ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());

    // create a list to add files to be zipped
    ArrayList<File> files = new ArrayList<>(2);
    files.add(new File("README.md"));

    // package files
    for (File file : files) {
        //new zip entry and copying inputstream with file to zipOutputStream, after all closing streams
        zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
        FileInputStream fileInputStream = new FileInputStream(file);

        IOUtils.copy(fileInputStream, zipOutputStream);

        fileInputStream.close();
        zipOutputStream.closeEntry();
    }    

    zipOutputStream.close();
}
Run Code Online (Sandbox Code Playgroud)

  • 因为此解决方案将数据直接流式传输到响应输出流。 (2认同)

ces*_*sar 12

@RequestMapping(value="/zip", produces="application/zip")
public ResponseEntity<StreamingResponseBody> zipFiles() {
    return ResponseEntity
            .ok()
            .header("Content-Disposition", "attachment; filename=\"test.zip\"")
            .body(out -> {
                var zipOutputStream = new ZipOutputStream(out);

                // create a list to add files to be zipped
                ArrayList<File> files = new ArrayList<>(2);
                files.add(new File("README.md"));

                // package files
                for (File file : files) {
                    //new zip entry and copying inputstream with file to zipOutputStream, after all closing streams
                    zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
                    FileInputStream fileInputStream = new FileInputStream(file);

                    IOUtils.copy(fileInputStream, zipOutputStream);

                    fileInputStream.close();
                    zipOutputStream.closeEntry();
                }

                zipOutputStream.close();
            });
}
Run Code Online (Sandbox Code Playgroud)