使用 FileChannel 或 Files.copy 将文件复制到 jar 中



Use Java 8.

为了获得最佳性能,我尝试使用Files.copy()复制文件,但很快就找到了它 不支持汉字。例如:

try {
Files.copy(
Objects.requireNonNull(
Main.class.getResourceAsStream("/amres/core/template.xlsx")),
Paths.get("C:/我的/test.xlsx"), // "我的" means mine in Chinese
StandardCopyOption.REPLACE_EXISTING
);
} catch (IOException e) {
e.printStackTrace();
}

该代码打算从 jar 复制文件,但它抛出异常("我的"文件夹有 提前创建):java.nio.file.NoSuchFileException: C:鎴戠殑test.xlsx
问题是,"鎴戠殑"甚至不能被中国人理解,所以我正在寻找一个 处理汉字的解决方案。

我也尝试过FileChannel,但失败了,意识到它用于直接文件,而不是文件 在一个罐子里。我该怎么办?

你吠错了树。Files.copy与支持(或不支持)中文字符没有任何关系,Java确实支持完整的Unicode路径名。是的,很明显您的代码当前没有按设计工作,并且可以修复,但问题并不Files.copy

旁注:您的代码已损坏

Main.class.getResourceAsStream是从代码库中提取资源的正确方法,但是,这是一个资源,因此,您必须将其关闭。把它包裹在一个尝试块中,这是聪明的方法。

Objects.requireNonNull不应该在这里使用- 其目的是强制抛出一个 NullPointerException。这就是它所做的一切。如果以某种方式缺少该资源,此代码将已经抛出 NPE。这意味着 requireNonNull 是完全没有意义的(它正在强制执行已经发生的事情),如果你想要干净的代码,任何一个都是不合适的:你应该重新抛出一个例外,以正确传达应用程序已损坏的概念。

但是,这也不是一个好主意:我们不会为错误或中断的部署抛出异常。如果你认为你应该这样做,那么你应该用一个try/catch块包装整个java项目中的每一行代码,毕竟,显然我们不能假设任何事情。我们甚至不能假设java.lang.String在运行时可用 - 显然这不是一个可持续的观点。换句话说,您可以放心地假设资源不可能不存在异常流。

因此,我们得到了这个更简单、更安全的代码:

try (var in = Main.class.getResourceAsStream("/amres/core/template.xlsx")) {
Files.copy(in, Paths.get("C:/我的/test.xlsx"), StandardCopyOption.REPLACE_EXISTING);
}

请注意,一般来说,在所有情况下,捕获异常并使用e.printStackTrace()处理它也很糟糕:您将异常打印到它可能不应该去的地方,抛出有用的信息(如因果链),然后让代码异常继续,即使代码的状态显然处于意外状态,因此未知状态。最好的通用解决方案是实际throws异常。如果这不可行,或者您现在不想关心它,因此依赖于 IDE 的自动修复并且懒得编辑任何内容,那么至少修复 IDE 的自动修复器以发出非愚蠢的代码。throw new RuntimeException("uncaught", e)是适当的"我现在不想关心这个"填充。因此,请修复您的 IDE。它通常在设置中的"模板"下。

可能导致这种情况的原因是什么

每次字符转换为字节(反之亦然)时,都会涉及字符集编码。文件名看起来像字符,当然当你编写代码时,你正在写字符,当你看到NoSuchFileException的文本时,那就是字符 - 但是介于两者之间的所有东西呢?此外,文件系统本身的名称不清楚:某些文件系统名称是基于字节的。例如,Apple的APFS完全基于字节。文件名只是字节,并且将这些字节呈现到屏幕上的想法(并翻译例如touch foobar.txt在命令行上到文件名的字节序列值上)仅按照约定使用 UTF-8 完成。相比之下,某些文件系统将集合编码的概念直接编码到其 API 中。最好的办法是UTF_8所有的事情,这是事情出错的可能性最小。

因此,让我们完成以下步骤:

  1. 您可以在文本编辑器中编写 Java 代码。你写字符。
  2. 文件内容通常基于所有文件系统的字节。因此,当您在文本编辑器中点击"保存"快捷方式时,您的字符将转换为字节。检查编辑器是否配置为 UTF-8 模式。或者,使用反斜杠-u 转义来避免此问题
  3. 编译此代码。可能是javacecj或基于这两者之一的东西。它们读取文件(因此,字节),但将输入解析为字符,因此正在发生转换。确保使用--encoding UTF-8参数调用javac/ecj。如果使用 maven 或 gradle 等构建工具,请确保已显式配置。
  4. 该代码将运行并将其错误打印到控制台。控制台会向您显示它。控制台正在将字节(因为应用输出是基于字节的流)转换为字符,以便向您显示。它是否配置为使用 UTF-8 执行此操作?检查终端应用程序的设置。

检查所有粗体项目,95%+ 机会通过这样做来解决您的问题。

在将 Java 编译为类文件时,您可能使用了错误的文件编码。如果您在使用值之前简单地打印出文件的路径名,那么这个问题应该很清楚:

System.out.println("C:/我的/test.xlsx");

如果该路径未被识别为中文文件夹名称,请尝试使用javac -encoding标志再次编译以匹配源文件的格式。

最新更新