Java将文件复制到zip AccessDeniedException中

时间:2018-01-26 19:15:02

标签: java multithreading file java-8 zip

描述:

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

Files.copy

问题:

每当我添加行java.nio.file.AccessDeniedException: .\backups\test.zip以将我的目录以及所有子目录和子文件复制到zip文件中时,我都会遇到以下异常: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

我注意到,每当我调用NoSuchFileException方法时,ZIP文件甚至都没有创建或者至少没有保存,因此AccessDeniedException在抛出function Get-Customer { [CmdletBinding()] Param( [string]$CustomerName = "*" ) Process { # real process iterate on fodlers. New-Object PSObject -Property @{ CustomerName = $CustomerName; } } } function Get-Device { [CmdletBinding()] Param( [Parameter(ValueFromPipelineByPropertyName)] [string]$CustomerName = "*", [string]$DeviceName = "*" ) Process { # real process iterate on fodlers. New-Object PSObject -Property @{ CustomerName=$CustomerName; DeviceName=$DeviceName } } } 之后抛出我尝试复制的每个目录和文件。

3 个答案:

答案 0 :(得分:3)

我从未使用过java.nio.file,但是一旦我不得不处理这样的任务而且我使用了java.util.zip,这非常简单,仅用于从目录创建zip文件

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

  • 使用新的ZipOutputStream
  • 创建新的ZIP存档
  • 浏览将使用Files.walk
  • 压缩的文件树
  • 对于文件树的每个路径,包括条目。 ZipEntry保存有关归档中单个文件的元数据

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

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();
}

答案 1 :(得分:1)

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

在这一行

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方法的参数,因此您必须解析目标路径,如< / p>

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
}

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

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

答案 2 :(得分:0)

前段时间我发布了一些实用程序类,用于使用NIO.2 File API向/从JAR / ZIP文件添加和解压缩文件。

这是教程中的一个片段:

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>