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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27952949/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 05:21:24  来源:igfitidea点击:

Spring REST - create .zip file and send it to the client

javaspringrestzipzipoutputstream

提问by azalut

I want to create .zip file that contains my zipped files that I recieve from backend, and then send this file to the user. For 2 days I have been looking for the answer and cant find proper solution, maybe you can help me :)

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

For now, the code is like this:(I know I shouldnt do it all in the spring controller, but dont care about that, it is just for testing purposes, to find the way to make it works)

目前,代码是这样的:(我知道我不应该在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();
    }

But the problem is, that using the code, when I enter URL: localhost:8080/zip I get file: test.zip.html instead of .zip file

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

When I remove .html extension and leave just test.zip it opens correctlyhow to avoid returning this .html extension? why is it added?

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

I have no idea what else can I do. I was also trying replace ByteArrayOuputStream with something like:

我不知道我还能做什么。我还尝试用以下内容替换 ByteArrayOuputStream:

OutputStream outputStream = response.getOutputStream();

and set the method to be voidso it returns nothing, but It created .zip file which was.. damaged?

并将该方法设置为无效,因此它不返回任何内容,但它创建了 .zip 文件,该文件已损坏?

On my macbook after unpacking the test.zipI was getting test.zip.cpgzwhich was again giving me test.zip file and so on..

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

On windows the .zip file was damaged as I said and couldn't even open it.

在 Windows 上,.zip 文件如我所说已损坏,甚至无法打开。

I also suppose, that removing .html extension automatically will be the best option, but how? Hope it is no as hard as It seems to be :) thanks

我也想,自动删除 .html 扩展名将是最好的选择,但是如何呢?希望它不像看起来那么难:)谢谢

回答by azalut

seems to be solved. I replaced:

似乎解决了。我替换了:

response.setContentType("application/zip");

with:

和:

@RequestMapping(value = "/zip", produces="application/zip")

And now I get clear, beautiful .zip file :)

现在我得到了清晰、漂亮的 .zip 文件 :)

If any of you have either better or faster proposition, or just want to give some advice, then go ahead, I am curious.

如果你们中有人有更好或更快的提议,或者只是想提供一些建议,那么请继续,我很好奇。

回答by denov

@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();
}

回答by Raf

I am using REST Web Serviceof Spring Bootand I have designed the endpoints to always return ResponseEntitywhether it is JSONor PDFor ZIPand I came up with the following solution which is partially inspired by denov's answerin this question as well as another questionwhere I learned how to convert ZipOutputStreaminto byte[]in order to feed it to ResponseEntityas output of the endpoint.

我使用REST Web ServiceSpring Boot,我已经设计了端点总是返回ResponseEntity无论是JSONPDFZIP和我想出了以下解决方案,它通过部分灵感denov's answer在这个问题以及其他问题,我学到了如何转换ZipOutputStreambyte[]以饲料它ResponseEntity作为端点的输出。

Anyway, I created a simple utility class with two methods for pdfand zipfile download

无论如何,我创建了一个简单的实用程序类有两个方法pdfzip文件下载

@Component
public class FileUtil {
    public BinaryOutputWrapper prepDownloadAsPDF(String filename) throws IOException {
        Path fileLocation = Paths.get(filename);
        byte[] data = Files.readAllBytes(fileLocation);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/pdf"));
        String outputFilename = "output.pdf";
        headers.setContentDispositionFormData(outputFilename, outputFilename);
        headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");

        return new BinaryOutputWrapper(data, headers); 
    }

    public BinaryOutputWrapper prepDownloadAsZIP(List<String> filenames) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/zip"));
        String outputFilename = "output.zip";
        headers.setContentDispositionFormData(outputFilename, outputFilename);
        headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");

        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        ZipOutputStream zipOutputStream = new ZipOutputStream(byteOutputStream);

        for(String filename: filenames) {
            File file = new File(filename); 
            zipOutputStream.putNextEntry(new ZipEntry(filename));           
            FileInputStream fileInputStream = new FileInputStream(file);
            IOUtils.copy(fileInputStream, zipOutputStream);
            fileInputStream.close();
            zipOutputStream.closeEntry();
        }           
        zipOutputStream.close();
        return new BinaryOutputWrapper(byteOutputStream.toByteArray(), headers); 
    }
}

And now the endpoint can easily return ResponseEntity<?>as shown below using the byte[]data and custom headers that is specifically tailored for pdfor zip.

现在端点可以ResponseEntity<?>使用byte[]专门为pdfor量身定制的数据和自定义标头,如下所示轻松返回zip

@GetMapping("/somepath/pdf")
public ResponseEntity<?> generatePDF() {
    BinaryOutputWrapper output = new BinaryOutputWrapper(); 
    try {
        String inputFile = "sample.pdf"; 
        output = fileUtil.prepDownloadAsPDF(inputFile);
        //or invoke prepDownloadAsZIP(...) with a list of filenames
    } catch (IOException e) {
        e.printStackTrace();
        //Do something when exception is thrown
    } 
    return new ResponseEntity<>(output.getData(), output.getHeaders(), HttpStatus.OK); 
}

The BinaryOutputWrapperis a simple immutable POJOclass I created with private byte[] data;and org.springframework.http.HttpHeaders headers;as fields in order to return both dataand headersfrom utility method.

BinaryOutputWrapper是一个简单的不可变POJO类,我使用private byte[] data;org.springframework.http.HttpHeaders headers;作为字段创建,以便从实用程序方法返回dataheaders

回答by cesar

@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();
            });
}