簡體   English   中英

Spring REST - 創建 ZIP 文件並將其發送到客戶端

[英]Spring REST - create ZIP file and send it to the client

我想創建一個 ZIP 文件,其中包含我從后端收到的存檔文件,然后將此文件發送給用戶。 2天來我一直在尋找答案,但找不到合適的解決方案,也許你可以幫助我:)

現在,代碼是這樣的(我知道我不應該在 Spring 控制器中做所有的事情,但不要在意,它只是為了測試目的,找到讓它工作的方法):

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

        // Creating byteArray stream, make it bufferable and passing this buffer 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();
    }

但問題是,使用代碼,當我輸入 URL localhost:8080/zip時,我得到一個文件test.zip.html而不是.zip文件。

當我刪除.html擴展名並僅保留test.zip時,它會正確打開。 所以我的問題是:

  • 如何避免返回此.html擴展名?
  • 為什么要添加它?

我不知道我還能做什么。 我還嘗試將ByteArrayOuputStream替換為:

OutputStream outputStream = response.getOutputStream();

並將方法設置為無效,因此它不返回任何內容,但它創建了損壞的.zip文件?

在我的 MacBook 上解壓test.zip后,我得到了test.zip.cpgz ,它再次給了我test.zip文件等等。

正如我所說,在 Windows 上,.zip 文件已損壞,甚至無法打開。

我還認為,自動刪除.html擴展名將是最好的選擇,但是如何呢?

希望它不像看起來那么難:)
謝謝

問題已經解決了。

我更換了:

response.setContentType("application/zip");

和:

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

現在我得到了一個清晰、漂亮的.zip文件。


如果你們中的任何人有更好或更快的提議,或者只是想提供一些建議,那么請繼續,我很好奇。

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

我正在使用Spring BootREST Web Service ,並且我將端點設計為始終返回ResponseEntity ,無論它是JSONPDF還是ZIP ,我想出了以下解決方案,該解決方案部分受到denov's answer在這個問題和另一個問題中的回答的啟發我在那里學習了如何將ZipOutputStream轉換為byte[]以便將其作為端點的輸出提供給ResponseEntity

無論如何,我創建了一個簡單的實用程序類,有兩種方法用於下載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); 
    }
}

現在,端點可以使用專門為pdfzip定制的byte[]數據和自定義標頭輕松返回ResponseEntity<?> ,如下所示。

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

BinaryOutputWrapper是一個簡單的不可變POJO類,我使用private byte[] data; org.springframework.http.HttpHeaders headers; 作為字段,以便從實用程序方法返回dataheaders

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM