gpt4 book ai didi

无需使用 byte[] 即可立即从流中提取 Java zip 文件

转载 作者:行者123 更新时间:2023-11-30 10:08:09 30 4
gpt4 key购买 nike

我想将多个文件压缩成一个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();
}

在压缩所有文件然后开始下载之前,这会花费很多时间,对于大文件,这会花费很多时间,这是造成延迟的行:

while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}

那么有没有办法在压缩文件的同时立即下载文件?

最佳答案

试试这个。与其使用 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。

关于无需使用 byte[] 即可立即从流中提取 Java zip 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53885467/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com