Sure, here is the translation: 你可以在一个程序中使用两个Java NIO文件系统吗?

huangapple go评论86阅读模式
英文:

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) 具体 FileSystemgetPath(...) 方法,路径将与该文件系统相关联。

所以你应该使用这三种方式之一来控制应与路径相关联的文件系统,例如:

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

huangapple
  • 本文由 发表于 2020年8月27日 16:16:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/63611874.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定