我正在创建一个命令行应用程序,需要将一些文件(多个文件)输出到ZIP文件或普通文件夹中,具体取决于给定的参数。
我的方法是使用FileSystem
封装目标(普通文件夹/ ZIP文件)。
我的问题是我不能成功为当前工作目录以外的目录创建一个FileSystem
对象,表示我的硬盘上的绝对路径:
public class FileSystemWriteTest {
public static void main(String[] args) throws IOException {
Path absolutePath = Paths.get("target", "testpath").toAbsolutePath();
System.out.println(String.format("user.dir before change:\n %s", System.getProperty("user.dir")));
System.setProperty("user.dir", absolutePath.toString());
System.out.println(String.format("changed user.dir:\n %s", System.getProperty("user.dir")));
FileSystem defaultSystem = FileSystems.getDefault();
Path testFilePath = defaultSystem.getPath("test.file");
System.out.println(String.format("expected to be in changed user.dir:\n %s", testFilePath.toAbsolutePath()));
URI uri = absolutePath.toUri();
System.out.println(String.format("URI: %s", uri));
FileSystem localFileSystem =
FileSystems.newFileSystem(uri, Collections.emptyMap());
Path file = localFileSystem.getPath("test.txt");
System.out.println(file.toAbsolutePath());
}
}
输出是:
user.dir before change:
D:\data\scm-workspace\anderes\Test
changed user.dir:
D:\data\scm-workspace\anderes\Test\target\testpath
expected to be in changed user.dir:
D:\data\scm-workspace\anderes\Test\test.file
URI: file:///D:/data/scm-workspace/anderes/Test/target/testpath/
Exception in thread "main" java.lang.IllegalArgumentException: Path component should be '/'
at sun.nio.fs.WindowsFileSystemProvider.checkUri(Unknown Source)
at sun.nio.fs.WindowsFileSystemProvider.newFileSystem(Unknown Source)
at java.nio.file.FileSystems.newFileSystem(Unknown Source)
at java.nio.file.FileSystems.newFileSystem(Unknown Source)
at com.oc.test.filesystem.FileSystemWriteTest.main(FileSystemWriteTest.java:27)
如果我更改为FileSystems.newFileSystem(Path, Classloader)
,则异常更改为:
Exception in thread "main" java.nio.file.ProviderNotFoundException: Provider not found
at java.nio.file.FileSystems.newFileSystem(Unknown Source)
at com.oc.test.filesystem.FileSystemWriteTest.main(FileSystemWriteTest.java:27)
看起来这只适用于常规文件,而不适用于目录。
那么如何为除pwd之外的目录创建FileSystem
对象?
没有用FileSystem
像语义来创建chroot
的构建工具。默认文件系统仅支持file:///
作为URI,并且不允许多个实例化。
在这方面,FileSystems.getDefault().getPath("test.file")
创造了一条相对路径,就像Paths.get("test.file")
一样。为默认文件系统和其他文件系统创建的相对路径之间的差异在于没有指定其他基本路径时的解析行为(例如,在调用toAbsolutePath()
或只是尝试打开它们时)。但是针对当前工作目录进行解析并不会使它们成为根路径。
实现文件系统不可知操作的最佳解决方案是让代码接收基本的Path
对象,以解析相对路径。
例如。一个简单的树复制例程可能看起来像:
static void copyTree(Path sourceBase, Path targetBase) throws IOException {
try {
Files.walk(sourceBase).forEach(path -> {
if(Files.isRegularFile(path)) try {
Path target = targetBase.resolve(sourceBase.relativize(path).toString());
if(!Files.isDirectory(target.getParent()))
Files.createDirectories(target.getParent());
Files.copy(path, target, StandardCopyOption.COPY_ATTRIBUTES);
} catch(IOException ex) {
throw new UncheckedIOException(ex);
}
});
} catch(UncheckedIOException ex) {
throw ex.getCause();
}
}
对于这种方法,无论是从硬盘目录复制到另一个硬盘目录还是压缩文件系统,还是从zip文件系统复制到硬盘驱动器,或从zip文件复制到另一个zip文件等,都无关紧要。
最有趣的部分是调用sourceBase.relativize(path)
,以获取从源基本路径到实际文件的子路径的相对路径。由于相对Path
实例仍然与特定文件系统相关联,因此代码在将toString()
传递给targetBase.resolve(…)
之前调用path.resolve(string)
,以确保它可以在不同的文件系统中工作。请注意,path.resolve(path.getFileSystem().getPath(string))
相当于Path
。如果方法首先检查两个String
实例是否属于同一个文件系统,那么在这种情况下跳过qazxswpoi绕道是合法的。