Spring REST - 创建 .zip 文件并将其发送到客户端
Posted
技术标签:
【中文标题】Spring REST - 创建 .zip 文件并将其发送到客户端【英文标题】:Spring REST - create .zip file and send it to the client 【发布时间】:2015-03-13 05:27:28 【问题描述】:我想创建 .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();
但问题是,使用代码,当我输入 URL:localhost:8080/zip 时,我得到文件:test.zip.html 而不是 .zip 文件
当我删除 .html 扩展名并仅保留 test.zip 时,它会正确打开如何避免返回此 .html 扩展名?为什么要添加?
我不知道我还能做什么。我也尝试用类似的东西替换 ByteArrayOutputStream:
OutputStream outputStream = response.getOutputStream();
并将方法设置为 void 所以它什么都不返回,但是它创建了.zip 文件,它是..损坏的?
在我的 macbook 上解压 test.zip 后,我得到了 test.zip.cpgz,它再次给了我 test.zip 文件等等..
如我所说,在 Windows 上 .zip 文件已损坏,甚至无法打开。
我还认为,自动删除 .html 扩展名将是最好的选择,但如何呢? 希望它不像看起来那么难:)谢谢
【问题讨论】:
您可能会发现这里的答案很有帮助。***.com/a/68492465/3946706 【参考方案1】:@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();
);
【讨论】:
+1 帮助我弄清楚如何在不创建 HttpServletResponse 的情况下执行此操作,因为这似乎没有在 404 上设置状态代码。【参考方案2】:我正在使用Spring Boot
中的REST Web Service
并且我设计的端点总是返回ResponseEntity
无论是JSON
或PDF
还是ZIP
我想出了以下部分解决方案在这个问题中受到denov's answer
以及另一个question 的启发,我在其中学习了如何将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);
现在端点可以使用byte[]
数据和专门为pdf
或zip
定制的自定义标头轻松返回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
。
【讨论】:
ByteArrayOutputStream 将字节存储在内存中。所以对于大文件,你可以很快耗尽堆空间。【参考方案3】:@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();
【讨论】:
因为此解决方案将数据直接流式传输到响应输出流。 这是java服务器端代码,如何通过javascript在客户端下载?? @HarveyDent - 你应该问这个问题。 拜托,我被这个问题困扰了整整两天。提前致谢 你只需调用 url。在上面的代码中它将是 http://似乎解决了。我换了:
response.setContentType("application/zip");
与:
@RequestMapping(value = "/zip", produces="application/zip")
现在我得到了清晰、漂亮的 .zip 文件 :)
如果你们中的任何人有更好或更快的建议,或者只是想提供一些建议,那么请继续,我很好奇。
【讨论】:
+1 对于重要的问题并回答您自己的问题!我对这个实现的唯一问题/关注是最终的返回声明。由于toByteArray()
,我认为这会将整个文件加载到内存中。正确的?如果目录太大,我想知道是否有一种方法可以流式传输它以避免出现一些 OutOfMemoryException 错误。以上是关于Spring REST - 创建 .zip 文件并将其发送到客户端的主要内容,如果未能解决你的问题,请参考以下文章
在 Java Spring 的 REST 方法中返回后执行清理操作
创建文本文件并添加到zip文件并下载spring boot而不保存在本地服务器中
Spring REST Controller 没有响应 Angular 请求
Spring REST / Swagger / Postman - 正在下载损坏/空白文件
将 ZIP 文件发送到基于 REST 的 API,该 API 使用基于 Flutter 的移动应用程序托管在 AWS 上的 SSL TLS (https)