如何在 Java 中创建一个新的 zip 文件并向其中添加一个大目录?

Posted

技术标签:

【中文标题】如何在 Java 中创建一个新的 zip 文件并向其中添加一个大目录?【英文标题】:How can you create a new zip file in Java and add a large directory of files to it? 【发布时间】:2019-06-18 18:38:25 【问题描述】:

我正在尝试将文件目录添加到 zip。该目录大约有 150 个文件。在几个 5-75 个文件中,我不断崩溃并显示错误消息 "The process cannot access the file because it is being used by another process."

我尝试了延迟,这可能会有所帮助,但肯定不能解决错误。

使用以下代码: Is it possible to create a NEW zip file using the java FileSystem?

final File folder = new File("C:/myDir/img");
for (final File fileEntry : folder.listFiles()) 
    if (fileEntry.isDirectory()) 
        continue;
    
    else 
        String filename = fileEntry.getName();
        String toBeAddedName = "C:/myDir/img/" + filename;
        Path toBeAdded = FileSystems.getDefault().getPath(toBeAddedName).toAbsolutePath();
        createZip(zipLocation, toBeAdded, "./" + filename);
        System.out.println("Added file " + ++count);
        //Delay because 'file in use' bug
        try  Thread.sleep(1000);  //1secs
        catch (InterruptedException e) 
    


public static void createZip(Path zipLocation, Path toBeAdded, String internalPath) throws Throwable 
    Map<String, String> env = new HashMap<String, String>();
    //Check if file exists.
    env.put("create", String.valueOf(Files.notExists(zipLocation)));
    //Use a zip filesystem URI
    URI fileUri = zipLocation.toUri();  //Here
    URI zipUri = new URI("jar:" + fileUri.getScheme(), fileUri.getPath(), null);
    System.out.println(zipUri);
    //URI uri = URI.create("jar:file:"+zipLocation);    //Here creates the zip
    //Try with resource
    try (FileSystem zipfs = FileSystems.newFileSystem(zipUri, env)) 
        //Create internal path in the zipfs
        Path internalTargetPath = zipfs.getPath(internalPath);
        //Create parent dir
        Files.createDirectories(internalTargetPath.getParent());
        //Copy a file into the zip file
        Files.copy(toBeAdded, internalTargetPath, StandardCopyOption.REPLACE_EXISTING);
    

【问题讨论】:

如果文件因为正在使用而被锁定,除了向用户显示一条消息并要求他们更正之外,我看不出还有什么可以做的。 @markspace 锁定文件的是应用程序本身。这就是我尝试添加延迟的原因。 我现在在文件之间使用两秒钟的延迟,恕我直言,它很大但它正在工作。 如果可以阻止进程删除它本身已锁定的文件,我会觉得很奇怪(但我可能是错的)。您确定没有其他进程正在锁定文件吗?错误消息将表明是这种情况。你可以检查一下;例如,请参阅 this question (windows) 或 this question (linux)。 您是否有可能将 zip 添加到自身? 【参考方案1】:

我不能保证这是您的问题的原因,但是您的代码以一种奇怪的或至少低效的方式将文件压缩到 ZIP 文件中。具体来说,您要为每个要压缩的文件打开一个新的FileSystem。我假设您这样做是因为您链接到的问答就是这样做的。但是,that answer 仅压缩 一个 文件,而您想同时压缩 多个 文件。在压缩目录的整个过程中,您应该保持 FileSystem 处于打开状态。

public static void compress(Path directory, int depth, Path zipArchiveFile) throws IOException 
    var uri = URI.create("jar:" + zipArchiveFile.toUri());
    var env = Map.of("create", Boolean.toString(Files.notExists(zipArchiveFile, NOFOLLOW_LINKS)));

    try (var fs = FileSystems.newFileSystem(uri, env)) 
        Files.walkFileTree(directory, Set.of(), depth, new SimpleFileVisitor<>() 

            private final Path archiveRoot = fs.getRootDirectories().iterator().next();

            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException 
                // Don't include the directory itself
                if (!directory.equals(dir)) 
                    Files.createDirectory(resolveDestination(dir));
                
                return FileVisitResult.CONTINUE;
            

            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException 
                Files.copy(file, resolveDestination(file), REPLACE_EXISTING);
                return FileVisitResult.CONTINUE;
            

            private Path resolveDestination(Path path) 
                /*
                 * Use Path#resolve(String) instead of Path#resolve(Path). I couldn't find where the
                 * documentation mentions this, but at least three implementations will throw a 
                 * ProviderMismatchException if #resolve(Path) is invoked with a Path argument that 
                 * belongs to a different provider (i.e. if the implementation types don't match).
                 *
                 * Note: Those three implementations, at least in OpenJDK 12.0.1, are the JRT, ZIP/JAR,
                 * and Windows file system providers (I don't have access to Linux's or Mac's provider
                 * source currently).
                 */
                return archiveRoot.resolve(directory.relativize(path).toString());
            

        );
    

注意:depth 参数的使用方式与maxDepthFiles#walkFileTree 中的使用方式完全相同。

注意:如果您只关心目录本身中的文件(即不想递归遍历文件树),那么您可以使用Files#list(Path)。完成后不要忘记关闭Stream

您可能一遍又一遍地打开和关闭FileSystem 导致您的问题,在这种情况下,上述应该可以解决问题。

【讨论】:

以上是关于如何在 Java 中创建一个新的 zip 文件并向其中添加一个大目录?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 zip 文件归档器中创建文件夹 - node.js

如何在 Java 中创建未压缩的 Zip 存档

Java:如何使用 java.nio.file.FileSystem 在 zip 中创建目录

如何在python中创建一个zip文件

如何在内存中创建一个新的 java.io.File? [复制]

如何在 plsql (Oracle) 中创建 zip 文件夹