英文:
Can you use 2 java nio file systems in one program?
问题
以下是您要翻译的代码部分:
我正在编写一个项目,该项目将从模板文件夹结构生成一个文件夹结构(我将其作为资源打包在我的jar文件中)。我希望将所有资源保留在我的程序的jar文件中,这样我的用户只需知道这个jar文件,而不必担心单独的资源文件夹。
我有这段代码(使用java库的Scala代码):
```scala
package ru.company.project
import java.nio.file.{FileSystems, Files, Path, Paths}
import java.util
object Main {
def main(args: Array[String]): Unit = {
val uri = this.getClass.getClassLoader.getResource("resource1").toURI
// create file system
val env = new util.HashMap[String, String]()
env.put("create", "true")
val jarFS = FileSystems.newFileSystem(uri, env)
val file = Paths.get(uri)
Files.walk(file).forEach(f => {
println(f)
if (Files.isRegularFile(f))
copyToLocal(f)
})
jarFS.close()
}
def copyToLocal(file: Path): Unit = {
val content = Files.readAllLines(file)
Files.write(file, content)
}
}
它可以很好地从我的jar文件中读取资源,但是当在copyToLocal
中执行Files.write(file, content)
时,会出现以下异常:
Exception in thread "main" java.nio.file.FileAlreadyExistsException: resource1/another_text_file.txt
at com.sun.nio.zipfs.ZipFileSystem.newOutputStream(ZipFileSystem.java:516)
at com.sun.nio.zipfs.ZipPath.newOutputStream(ZipPath.java:790)
at com.sun.nio.zipfs.ZipFileSystemProvider.newOutputStream(ZipFileSystemProvider.java:285)
at java.nio.file.Files.newOutputStream(Unknown Source)
at java.nio.file.Files.write(Unknown Source)
at java.nio.file.Files.write(Unknown Source)
at ru.company.project.Main$.copyToLocal(Main.scala:28)
at ru.company.project.Main$.$anonfun$main$1(Main.scala:20)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.Iterator.forEachRemaining(Unknown Source)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.forEach(Unknown Source)
at ru.company.project.Main$.main(Main.scala:17)
at ru.company.project.Main.main(Main.scala:14)
这是因为我需要以某种方式使用另一个文件系统来写入jar文件外部,但似乎当您执行FilesSystems.newFileSystem
时,它会创建一个全局的文件系统。
那么,我如何创建另一个文件系统并同时使用两个文件系统?
英文:
I am writing a project that will generate a folder structure from a template folder structure (which I package inside my jar as a resource). I want to keep all the resources inside my program's jar file, so my users will only have to know about this one jar file, without having to worry about a separate resource folder.
I have this code here (Scala code using java libraries):
package ru.company.project
import java.nio.file.{FileSystems, Files, Path, Paths}
import java.util
object Main {
def main(args: Array[String]): Unit = {
val uri = this.getClass.getClassLoader.getResource("resource1").toURI
// create file system
val env = new util.HashMap[String, String]()
env.put("create", "true")
val jarFS = FileSystems.newFileSystem(uri, env)
val file = Paths.get(uri)
Files.walk(file).forEach(f => {
println(f)
if (Files.isRegularFile(f))
copyToLocal(f)
})
jarFS.close()
}
def copyToLocal(file: Path): Unit = {
val content = Files.readAllLines(file)
Files.write(file, content)
}
}
It is able to read the resources from my jar file just fine, but when it executes Files.write(file, content)
in copyToLocal
, it fails with this exception:
Exception in thread "main" java.nio.file.FileAlreadyExistsException: resource1/another_text_file.txt
at com.sun.nio.zipfs.ZipFileSystem.newOutputStream(ZipFileSystem.java:516)
at com.sun.nio.zipfs.ZipPath.newOutputStream(ZipPath.java:790)
at com.sun.nio.zipfs.ZipFileSystemProvider.newOutputStream(ZipFileSystemProvider.java:285)
at java.nio.file.Files.newOutputStream(Unknown Source)
at java.nio.file.Files.write(Unknown Source)
at java.nio.file.Files.write(Unknown Source)
at ru.company.project.Main$.copyToLocal(Main.scala:28)
at ru.company.project.Main$.$anonfun$main$1(Main.scala:20)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.Iterator.forEachRemaining(Unknown Source)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.forEach(Unknown Source)
at ru.company.project.Main$.main(Main.scala:17)
at ru.company.project.Main.main(Main.scala)
This is because I somehow need to use another file system to write outside the jar file, but it seems that when you do FilesSystems.newFileSystem
it creates a global one.
So, how do I create another file system and use 2 file systems at the same time?
答案1
得分: 3
你的方法 copyToLocal(...)
是错误的。它从文件中读取,然后将所有读取的行再次写回到同一个文件中。没有什么"魔法机制"能够"知道"在哪个文件系统上进行读取,在哪个文件系统上进行写入。
因此,主要问题是: 类似 Files.write(path, content)
这样的操作是如何知道要使用哪个 FileSystem
?
答案: 它使用创建路径时所使用的 FileSystem
。
更详细的回答: Path
对象总是与一个 FileSystem
相关联。它始终表示特定文件系统中的文件或文件夹,因此它既包含文件系统又包含路径本身。
Files
的所有静态方法,比如 Files.delete(path)
或 Files.list(path)
,都使用与给定路径相关联的文件系统。那些涉及多个路径的方法,比如 Files.copy(source, target, options)
,在两个路径都来自同一个文件系统时进行了优化(这是常见情况),但也可以在不同文件系统之间工作(就像你的情况)。
如果你使用 (1) 工厂方法 Paths.get(first, ...more)
,路径总是与默认的 FileSystem
相关联。如果你使用 (2) Paths.get(uri)
,会搜索所有已安装的文件系统提供者以获取文件系统。如果使用 (3) 具体 FileSystem
的 getPath(...)
方法,路径将与该文件系统相关联。
所以你应该使用这三种方式之一来控制应与路径相关联的文件系统,例如:
try(FileSystem jarFs = ...;
FileSystem defaultFs = ...) {
Path source = jarFs.getPath(...);
Path target = defaultFs.getPath(...);
Files.copy(source, target);
}
(代码片段为 Java,因为我不了解 Scala)
对于你的代码:
...
val jarPath = Paths.get(uri)
Files.walk(jarPath).forEach(jarFile => {
println(jarFile)
if (Files.isRegularFile(jarFile))
Files.copy(jarFile, createLocalPathFor(jarFile))
})
...
def createLocalPathFor(path: Path): Path = {
// TODO: 为给定文件创建本地路径
// TODO: 使用哪个本地文件夹?使用哪个本地文件名?
return Paths.get(...)
}
英文:
Your method copyToLocal(...)
is wrong. It reads from a file and then writes back all read lines to the same file again. There is no 'magic mechanism' that 'knows' which file system to use for reading and which for writing.
So the main question is: How does an operation like Files.write(path, content)
know, which FileSystem
to use?
Answer: It uses the FileSystem
that was used to create the path.
Longer answer: A Path
object is always associated to a FileSystem
. It always denotes a file or folder of a specific file system, so it holds both, the file system and the path itself.
All static methods of Files
, like Files.delete(path)
or Files.list(path)
use the file systems associated with the given path. Methods that operate on more than one path, like Files.copy(source, target, options)
are optimized in case both paths are from the same file system (which is the common case), but should also work across different file systems (as in your case).
If you use (1) the factory method Paths.get(first, ...more)
, the path is always associated to the default FileSystem
. If your use (2) Paths.get(uri)
, all installed file system providers are searched for the file system. If (3) the getPath(...)
method of a concrete FileSystem
is used, the path is associated with this file system.
So you should use any of these three ways to control the file system that should be associated to your path, e.g.:
try(FileSystem jarFs = ...;
FileSystem defaultFs = ...) {
Path source = jarFs.getPath(...);
Path target = defaultFs.getPath(...);
Files.copy(source, target);
}
(code snippet is in Java, as I don't know Scala)
This means for your code:
...
val jarPath = Paths.get(uri)
Files.walk(jarPath).forEach(jarFile => {
println(jarFile)
if (Files.isRegularFile(jarFile))
Files.copy(jarFile, createLocalPathFor(jarFile))
})
...
def createLocalPathFor(path: Path): Path = {
// TODO: create a local path for the given file
// TODO: which local folder? which local file name?
return Paths.get(...)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论