[英]AccessDeniedException on Files.copy from a temporary file in Java NIO2
[英]Java Copy files into zip AccessDeniedException
我已經用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
在下面的堆棧跟蹤中,我將類調用的行號更改為我上面發布的代碼片段的行號,以提高可讀性,但對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文件或至少沒有保存ZIP文件,因此,在為我嘗試復制的每個目錄和文件拋出AccessDeniedException
之后,將引發NoSuchFileException
。
我從未使用過java.nio.file,但是一旦我不得不處理此類任務,便使用了java.util.zip,它非常簡單,僅用於從目錄創建zip文件。
雖然,如果您無法更改用於歸檔目錄的內容,那么該解決方案將無濟於事,但示例代碼帶有一些解釋:
要使用它,只需調用帶有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();
}
您正在嘗試使用常規文件作為目錄。
在這條線
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
}
如果目標目錄存在且源也是目錄,則將跳過路徑條目,因為應通過異常報告源不是目錄但目標是現有目錄的情況。
如果要強制執行替換現有文件和目錄的策略,則在存在現有非空目錄的情況下,必須執行樹刪除操作,但是仍然必須跳過根目錄,而不能被刪除。
不久前,我發布了一些實用程序類,用於使用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解析跨文件系統的路徑。
有一個教程 。
該庫是開源的 ,可從Maven Central獲得:
<dependency>
<groupId>org.softsmithy.lib</groupId>
<artifactId>softsmithy-lib-core</artifactId>
<version>0.9</version>
</dependency>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.