Java 中的高效 Zip 文件流
Efficient Stream of Zip Files in Java
问题
在我的服务中,用户可以将非常大(5+ GB)的数据包下载为 zip 文件。当前的实现定位数据包中的所有文件,创建一个新的 zip 文件,用文件的副本填充 zip 文件,然后将其流式传输给用户。
对于较大的数据包,这无法很好地扩展,我正试图找到一种方法来提高此过程的效率。我在下面提出了一个解决方案,但我没有提供内容的经验,希望专业人士了解解决此问题的最佳方法。
尝试的解决方案
我认为最好的方法是不要将实际字节复制到 zip 文件中。相反,创建一个符号链接的压缩文件,然后在流式传输内容时复制字节。我在传输过程中实际将字节复制到 zip 中时遇到了问题,我不知道这是否可行。
结束解决方案
我将下面 Alexey Ragozin 接受的答案实施到 SpeedBagIt, a library for efficiently streaming zip files conforming to the BagIt 规范中。
您可以直接通过 HTTP 连接传输大型 zip 文件。
java.util.zip.ZipOutputStream
允许将数据直接压缩到流中而无需中间存储。
下面是将文件链接集合作为 zip 存档写入流的片段。
当然要流式传输 5GiB,您可能需要调整 servlet 容器的超时和线程池。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipStreamer {
public void streamZip(OutputStream os, Iterable<FileLink> entries) throws IOException {
ZipOutputStream zos = new ZipOutputStream(os);
for(FileLink e: entries) {
ZipEntry entry = new ZipEntry(e.getName());
File file = e.getFile();
entry.setTime(file.lastModified());
zos.putNextEntry(entry);
if (file.isFile()) {
copyBytes(zos, new FileInputStream(file));
}
zos.closeEntry();
}
zos.close();
}
private static void copyBytes(OutputStream dest, InputStream source) {
// copy data between streams
}
public interface FileLink {
public String getName();
public File getFile();
}
}
问题
在我的服务中,用户可以将非常大(5+ GB)的数据包下载为 zip 文件。当前的实现定位数据包中的所有文件,创建一个新的 zip 文件,用文件的副本填充 zip 文件,然后将其流式传输给用户。
对于较大的数据包,这无法很好地扩展,我正试图找到一种方法来提高此过程的效率。我在下面提出了一个解决方案,但我没有提供内容的经验,希望专业人士了解解决此问题的最佳方法。
尝试的解决方案
我认为最好的方法是不要将实际字节复制到 zip 文件中。相反,创建一个符号链接的压缩文件,然后在流式传输内容时复制字节。我在传输过程中实际将字节复制到 zip 中时遇到了问题,我不知道这是否可行。
结束解决方案
我将下面 Alexey Ragozin 接受的答案实施到 SpeedBagIt, a library for efficiently streaming zip files conforming to the BagIt 规范中。
您可以直接通过 HTTP 连接传输大型 zip 文件。
java.util.zip.ZipOutputStream
允许将数据直接压缩到流中而无需中间存储。
下面是将文件链接集合作为 zip 存档写入流的片段。
当然要流式传输 5GiB,您可能需要调整 servlet 容器的超时和线程池。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipStreamer {
public void streamZip(OutputStream os, Iterable<FileLink> entries) throws IOException {
ZipOutputStream zos = new ZipOutputStream(os);
for(FileLink e: entries) {
ZipEntry entry = new ZipEntry(e.getName());
File file = e.getFile();
entry.setTime(file.lastModified());
zos.putNextEntry(entry);
if (file.isFile()) {
copyBytes(zos, new FileInputStream(file));
}
zos.closeEntry();
}
zos.close();
}
private static void copyBytes(OutputStream dest, InputStream source) {
// copy data between streams
}
public interface FileLink {
public String getName();
public File getFile();
}
}