【问题标题】:Java Copy files into zip AccessDeniedExceptionJava 将文件复制到 zip AccessDeniedException
【发布时间】:2018-01-26 19:15:01
【问题描述】:

说明:

我在 Java 8 中创建了一个 ZIP 文件,并尝试将一个目录及其所有子文件和目录复制到这个 zip 文件中。

   Path directory = Paths.get("P:\Java\Test\backups\test.zip");
   // path to the world;
   Path world = Paths.get("P:\Java\Test\world");
   
   [...]
   
    // Create a map which tells the file system to create a new file if it doesn't exist
    ImmutableMap immutableMap = ImmutableMap.of("create", String.valueOf(Files.notExists(this.directory)));
    
    // Get a file system provider which is capable of creating a ZIP file
    FileSystemProvider zipProvider = FileSystemProvider.installedProviders().stream()
            .filter(provider -> provider.getScheme().equals("jar")).findFirst().get();
    
    // Create the file system
    try (FileSystem fs = zipProvider.newFileSystem(this.directory, immutableMap)) {
        
        try {
            Files.walk(this.world).forEach((Path sourcePath) -> {
                try {
                CopyOption[] option = new CopyOption[] {
                        StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES
                };
                Path destination = this.directory.resolve(this.world.relativize(sourcePath));
                Files.copy(sourcePath, destination,option);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

问题:

每当我添加行 Files.copy 以将我的目录以及所有子目录和子文件复制到 zip 文件中时,我都会收到以下异常:java.nio.file.AccessDeniedException: .\backups\test.zip

控制台输出:

在下面的堆栈跟踪中,我将类调用的行号更改为我在上面发布的代码 sn-p 的行号,以提高可读性,但对 ThreadBackup.run 方法的调用除外。它基本上是代码与其他一些不相关的东西一起执行的方法。

java.nio.file.AccessDeniedException: .\backups\tests.zip
    at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:83)
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
    at sun.nio.fs.WindowsFileCopy.copy(WindowsFileCopy.java:231)
    at sun.nio.fs.WindowsFileSystemProvider.copy(WindowsFileSystemProvider.java:278)
    at java.nio.file.Files.copy(Files.java:1274)
    at serverutilities.backups.ThreadBackups.lambda$createZipFile$1(ThreadBackups.java:24)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Iterator.forEachRemaining(Iterator.java:116)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
    at serverutilities.backups.ThreadBackups.createZipFile(ThreadBackups.java:18)
    at serverutilities.backups.ThreadBackups.run(ThreadBackups.java:56)
    at java.lang.Thread.run(Thread.java:748)

java.nio.file.NoSuchFileException: P:\Java\Test\backups\test.zip
    at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79)
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:90)
    at sun.nio.fs.WindowsLinkSupport.getRealPath(WindowsLinkSupport.java:259)
    at sun.nio.fs.WindowsPath.toRealPath(WindowsPath.java:836)
    at sun.nio.fs.WindowsPath.toRealPath(WindowsPath.java:44)
    at com.sun.nio.zipfs.ZipFileSystemProvider.removeFileSystem(ZipFileSystemProvider.java:322)
    at com.sun.nio.zipfs.ZipFileSystem.close(ZipFileSystem.java:305)
    at serverutilities.backups.ThreadBackups.createZipFile(ThreadBackups.java:32)
    at serverutilities.backups.ThreadBackups.run(ThreadBackups.java:56)
    at java.lang.Thread.run(Thread.java:748)

我注意到,每当我调用 Files.copy 方法时,ZIP 文件甚至都没有创建或至少没有保存,因此在为我尝试复制的每个目录和文件抛出 AccessDeniedException 之后抛出 NoSuchFileException .

【问题讨论】:

    标签: java multithreading file java-8 zip


    【解决方案1】:

    我从来没有使用过 java.nio.file,但是一旦我不得不处理这样的任务,我就使用了 java.util.zip,它非常简单地用于从目录创建一个 zip 文件

    虽然,如果您无法更改用于归档目录的内容,那么此解决方案不会有太大帮助,但示例代码和一些解释:

    • 使用新的 ZipOutputStream 创建新的 ZIP 存档
    • 遍历将使用 Files.walk 压缩的文件树
    • 为文件树的每个路径打包条目。 ZipEntry 保存有关存档中单个文件的元数据

    要使用它,只需调用带有 src 路径和 destination.zip 的方法 packDir

    private static void packDir(Path src, Path dest) throws IOException {
        try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(dest));
             ZipOutputStream zo = new ZipOutputStream(out);
             Stream<Path> dirStream = Files.walk(src)) {
             dirStream.filter(p -> !p.equals(src)).forEach(path -> {
                 try {
                     packEntry(src, zo, path);
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             });
        }
    }
    
    private static void packEntry(Path src, ZipOutputStream zo, Path path) throws IOException {
        String name = src.relativize(path).toString().replace('\\', '/');
        boolean isDir = Files.isDirectory(path);
        if (isDir) {
            name += "/";
        }
        ZipEntry e = new ZipEntry(name); 
        zo.putNextEntry(e);
        if (!isDir) {
            Files.copy(path, zo);
        }
        zo.closeEntry();
    }
    

    【讨论】:

    • 对我来说最好的解决方案是使用 java.nio.file 的解决方案,如果我找不到解决方案,我会看看是否可以更改我当前使用的解决方案。
    【解决方案2】:

    您正在尝试将常规文件用作目录。

    在这一行

    try (FileSystem fs = zipProvider.newFileSystem(this.directory, immutableMap)) {
    

    您正在this.directory 打开或创建一个 zip 文件系统,该文件系统必须是 默认 文件系统中的有效路径。成功后this.directory肯定是普通文件(zip文件格式),还在默认文件系统内。

    这一行

    Path destination = this.directory.resolve(this.world.relativize(sourcePath));
    

    将此常规文件视为目录。

    您要复制到 zip 文件系统中,因此您必须使用 zip 文件系统中的路径,而不是默认文件系统中 zip 文件的路径。

    您可以获取 zip 文件系统的根目录,例如

    Path zipRoot = fs.getPath("/");
    

    并以此为目标。据我所知,您不能使用从一个文件系统检索到的Path 作为另一个文件系统的Path 方法的参数,因此您必须像这样解析目标路径

    Path destination = zipRoot;
    for(Path p: this.world.relativize(sourcePath))
        destination = destination.resolve(p.toString());
    

    但也许有更简单的方法。


    另一个问题是将Files.copy 用于目录。当目录已经存在(并且根目录始终存在)时,它会失败,除非你指定REPLACE_EXISTING,但只要目标目录不为空,就会失败。这个最简单的解决方案是保持现有目录不变,所以代码看起来像

    try(FileSystem fs = zipProvider.newFileSystem(this.directory, immutableMap)) {
        Path zipRoot = fs.getPath("/");
        CopyOption[] option = {
            StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES
        };
        Files.walk(this.world).forEach(sourcePath -> {
            try {
                Path destination = zipRoot;
                for(Path p: this.world.relativize(sourcePath))
                    destination = destination.resolve(p.toString());
                if(!Files.isDirectory(destination) || !Files.isDirectory(sourcePath))
                    Files.copy(sourcePath, destination, option);
            } catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        });
    } catch(IOException|UncheckedIOException e) {
        e.printStackTrace(); // TODO replace with actual exception handling
    }
    

    这将跳过路径条目,如果目标目录存在并且源也是目录,因为源不是目录但目标是现有目录的情况应通过异常报告.

    如果要强制执行替换现有文件和目录的策略,则必须在存在现有非空目录的情况下执行树删除,但仍然必须跳过根目录,这无法删除。

    【讨论】:

    • 这几乎对我有用,以为我遇到了一些例外情况:java.nio.file.FileAlreadyExistsException: /java.nio.file.AccessDeniedException: .\backups\tests.zipjava.nio.file.NoSuchFileException: .\backups\tests.zip。这是我的复制行:Files.copy(sourcePath, destination);。因此,按照您的方式,我不再拥有我的 test.zip 文件,而是大约 4 到 5 个临时文件,其中包含所有应该复制到 zip 文件中的文件,并且可以使用 7Zip 之类的工具打开。如果您可以编辑答案以提供解决方案,那就太好了。
    • 我已经扩展了我的答案。我希望,这会有所帮助。
    【解决方案3】:

    前段时间,我发布了一些实用程序类,用于使用 NIO.2 文件 API 在 JAR/ZIP 文件中添加和提取文件。

    这是教程中的一个 sn-p:

    public void addResource(Path zipPath, Path targetDirPath, Path srcPath, String targetInZipPathString) throws IOException {  
        Path targetZipPath = copyZipFile(zipPath, targetDirPath);  
    
        try (FileSystem jarFS = JarFiles.newJarFileSystem(targetZipPath.toUri())) {  
            Path targetInZipPath = jarFS.getPath(targetInZipPathString);  
    
            // Adds the src directory name to the zip. You can omit this if you just want to copy the contents.  
            Path finalTargetInZipPath = PathUtils.resolve(targetInZipPath, srcPath.getFileName());  
            Files.createDirectories(finalTargetInZipPath);  
    
            CopyFileVisitor.copy(srcPath, finalTargetInZipPath);  
        }  
    }  
    

    CopyFileVisitor 使用PathUtils 来解析跨文件系统的路径。

    有一个tutorial

    库是 Open Source,可从 Maven Central 获得:

    <dependency>
        <groupId>org.softsmithy.lib</groupId>
        <artifactId>softsmithy-lib-core</artifactId>
        <version>0.9</version>
    </dependency>
    

    【讨论】:

      猜你喜欢
      • 2011-07-12
      • 1970-01-01
      • 2012-03-07
      • 1970-01-01
      • 2012-03-15
      • 2014-08-15
      • 2013-02-08
      • 2020-11-02
      • 2019-03-03
      相关资源
      最近更新 更多