[英]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 Boot
的REST Web Service
,並且我將端點設計為始終返回ResponseEntity
,無論它是JSON
、 PDF
還是ZIP
,我想出了以下解決方案,該解決方案部分受到denov's answer
在這個問題和另一個問題中的回答的啟發我在那里學習了如何將ZipOutputStream
轉換為byte[]
以便將其作為端點的輸出提供給ResponseEntity
。
無論如何,我創建了一個簡單的實用程序類,有兩種方法用於下載pdf
和zip
文件
@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);
}
}
現在,端點可以使用專門為pdf
或zip
定制的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;
作為字段,以便從實用程序方法返回data
和headers
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.