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
Spring REST - create .zip file and send it to the client
提问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 Service
of Spring Boot
and I have designed the endpoints to always return ResponseEntity
whether it is JSON
or PDF
or ZIP
and I came up with the following solution which is partially inspired by denov's answer
in this question as well as another questionwhere I learned how to convert ZipOutputStream
into byte[]
in order to feed it to ResponseEntity
as output of the endpoint.
我使用REST Web Service
的Spring Boot
,我已经设计了端点总是返回ResponseEntity
无论是JSON
或PDF
或ZIP
和我想出了以下解决方案,它通过部分灵感denov's answer
在这个问题以及其他问题,我学到了如何转换ZipOutputStream
为byte[]
以饲料它ResponseEntity
作为端点的输出。
Anyway, I created a simple utility class with two methods for pdf
and zip
file download
无论如何,我创建了一个简单的实用程序类有两个方法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);
}
}
And now the endpoint can easily return ResponseEntity<?>
as shown below using the byte[]
data and custom headers that is specifically tailored for pdf
or zip
.
现在端点可以ResponseEntity<?>
使用byte[]
专门为pdf
or量身定制的数据和自定义标头,如下所示轻松返回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 BinaryOutputWrapper
is a simple immutable POJO
class I created with private byte[] data;
and org.springframework.http.HttpHeaders headers;
as fields in order to return both data
and headers
from utility method.
这BinaryOutputWrapper
是一个简单的不可变POJO
类,我使用private byte[] data;
和org.springframework.http.HttpHeaders headers;
作为字段创建,以便从实用程序方法返回data
和headers
。
回答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();
});
}