java.nio
包具有一种处理zip文件的漂亮方式,可以将它们视为文件系统。这使我们能够像普通文件一样处理zip文件内容。因此,只需使用Files.copy
将所有文件复制到zip文件中即可实现整个文件夹的压缩。由于子文件夹也要复制,我们需要访问者:
private static class CopyFileVisitor extends SimpleFileVisitor<Path> {
private final Path targetPath;
private Path sourcePath = null;
public CopyFileVisitor(Path targetPath) {
this.targetPath = targetPath;
}
@Override
public FileVisitResult preVisitDirectory(final Path dir,
final BasicFileAttributes attrs) throws IOException {
if (sourcePath == null) {
sourcePath = dir;
} else {
Files.createDirectories(targetPath.resolve(sourcePath
.relativize(dir).toString()));
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(final Path file,
final BasicFileAttributes attrs) throws IOException {
Files.copy(file,
targetPath.resolve(sourcePath.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
}
这是一个简单的&#34;复制目录递归&#34;游客。它用于递归复制目录。但是,使用ZipFileSystem
,我们也可以使用它将目录复制到zip文件中,如下所示:
public static void zipFolder(Path zipFile, Path sourceDir) throws ZipException, IOException
{
// Initialize the Zip Filesystem and get its root
Map<String, String> env = new HashMap<>();
env.put("create", "true");
URI uri = URI.create("jar:" + zipFile.toUri());
FileSystem fileSystem = FileSystems.newFileSystem(uri, env);
Iterable<Path> roots = fileSystem.getRootDirectories();
Path root = roots.iterator().next();
// Simply copy the directory into the root of the zip file system
Files.walkFileTree(sourceDir, new CopyFileVisitor(root));
}
这就是我称之为压缩整个文件夹的优雅方式。但是,当在一个巨大的文件夹(大约3 GB)上使用此方法时,我收到OutOfMemoryError
(堆空间)。使用通常的zip处理库时,不会引发此错误。因此,似乎ZipFileSystem
处理副本的方式效率非常低:要写入的文件太多会保留在内存中,因此会出现OutOfMemoryError
。
为什么会这样?使用ZipFileSystem
通常被认为效率低下(就内存消耗而言)还是我在这里做错了什么?
答案 0 :(得分:25)
我查看了ZipFileSystem.java,我相信我找到了内存消耗的来源。默认情况下,实现使用ByteArrayOutputStream
作为压缩文件的缓冲区,这意味着它受分配给JVM的内存量的限制。
我们可以使用(未记录的)环境变量来使实现使用临时文件("useTempFile"
)。它的工作原理如下:
Map<String, Object> env = new HashMap<>();
env.put("create", "true");
env.put("useTempFile", Boolean.TRUE);
此处有更多详情:http://www.docjar.com/html/api/com/sun/nio/zipfs/ZipFileSystem.java.html,有趣的行是96,1358和1362。
答案 1 :(得分:-2)
您必须准备jvm以允许-Xms {memory} -Xmx {memory}
的那些内存量。
我建议你检查目录计算磁盘空间并设置一个限制,在1Gb下使用内存文件系统,超过1GB使用磁盘文件系统。
另一件事,检查方法的并发性,你不喜欢超过1个线程压缩3Gb的文件