我想将多个文件压缩为zip文件,我正在处理大文件,然后将它们下载到客户端,目前我正在使用此文件:
@RequestMapping(value = "/download", method = RequestMethod.GET, produces = "application/zip")
public ResponseEntity <StreamingResponseBody> getFile() throws Exception {
File zippedFile = new File("test.zip");
FileOutputStream fos = new FileOutputStream(zippedFile);
ZipOutputStream zos = new ZipOutputStream(fos);
InputStream[] streams = getStreamsFromAzure();
for (InputStream stream: streams) {
addToZipFile(zos, stream);
}
final InputStream fecFile = new FileInputStream(zippedFile);
Long fileLength = zippedFile.length();
StreamingResponseBody stream = outputStream - >
readAndWrite(fecFile, outputStream);
return ResponseEntity.ok()
.header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + "download.zip")
.contentLength(fileLength)
.contentType(MediaType.parseMediaType("application/zip"))
.body(stream);
}
private void addToZipFile(ZipOutputStream zos, InputStream fis) throws IOException {
ZipEntry zipEntry = new ZipEntry(generateFileName());
zos.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
zos.closeEntry();
fis.close();
}
在压缩所有文件然后开始下载之前,这需要花费很多时间,对于大型文件,此kan需要花费很多时间,这是造成延迟的原因:
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
有没有办法在压缩文件的同时立即下载文件?
答案 0 :(得分:1)
试试看。而不是使用ZipOutputStream
来包装FileOutputStream
,而是将zip写入文件,然后将其复制到客户端输出流,而不是使用ZipOutputStream
来包装客户端输出流,这样当您添加zip条目和数据时,它将直接发送到客户端。如果您还想也将其存储到服务器上的文件中,则可以将ZipOutputStream
写入拆分输出流,以一次写入两个位置。
@RequestMapping(value = "/download", method = RequestMethod.GET, produces = "application/zip")
public ResponseEntity<StreamingResponseBody> getFile() throws Exception {
InputStream[] streamsToZip = getStreamsFromAzure();
// You could cache already created zip files, maybe something like this:
// String[] pathsOfResourcesToZip = getPathsFromAzure();
// String zipId = getZipId(pathsOfResourcesToZip);
// if(isZipExist(zipId))
// // return that zip file
// else do the following
StreamingResponseBody streamResponse = clientOut -> {
FileOutputStream zipFileOut = new FileOutputStream("test.zip");
ZipOutputStream zos = new ZipOutputStream(new SplitOutputStream(clientOut, zipFileOut));
for (InputStream in : streamsToZip) {
addToZipFile(zos, in);
}
};
return ResponseEntity.ok()
.header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + "download.zip")
.contentType(MediaType.parseMediaType("application/zip")).body(streamResponse);
}
private void addToZipFile(ZipOutputStream zos, InputStream fis) throws IOException {
ZipEntry zipEntry = new ZipEntry(generateFileName());
zos.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
zos.closeEntry();
fis.close();
}
public static class SplitOutputStream extends OutputStream {
private final OutputStream out1;
private final OutputStream out2;
public SplitOutputStream(OutputStream out1, OutputStream out2) {
this.out1 = out1;
this.out2 = out2;
}
@Override public void write(int b) throws IOException {
out1.write(b);
out2.write(b);
}
@Override public void write(byte b[]) throws IOException {
out1.write(b);
out2.write(b);
}
@Override public void write(byte b[], int off, int len) throws IOException {
out1.write(b, off, len);
out2.write(b, off, len);
}
@Override public void flush() throws IOException {
out1.flush();
out2.flush();
}
/** Closes all the streams. If there was an IOException this throws the first one. */
@Override public void close() throws IOException {
IOException ioException = null;
for (OutputStream o : new OutputStream[] {
out1,
out2 }) {
try {
o.close();
} catch (IOException e) {
if (ioException == null) {
ioException = e;
}
}
}
if (ioException != null) {
throw ioException;
}
}
}
对于第一个要压缩的资源集的请求,您将不知道生成的zip文件的大小,因此您无法将长度和响应一起发送,因为您在压缩文件时正在流式传输文件。
但是,如果您希望重复请求压缩同一组资源,则可以缓存zip文件,并在随后的任何请求中简单地将它们返回;您还将知道缓存的zip文件的长度,因此也可以在响应中发送该文件。
如果要执行此操作,则必须能够为要压缩的资源的每种组合一致地创建相同的标识符,以便可以检查这些资源是否已压缩,并返回缓存的文件(如果它们已被压缩)。是。您也许可以对将要压缩的资源的ID(可能是完整路径)进行排序,然后将它们连接起来以创建zip文件的ID。