如何在 Java 中安全地编码字符串以用作文件名?

新手上路,请多包涵

我从外部进程接收到一个字符串。我想用那个字符串来创建一个文件名,然后写入那个文件。这是我执行此操作的代码片段:

     String s = ... // comes from external source
    File currentFile = new File(System.getProperty("user.home"), s);
    PrintWriter currentWriter = new PrintWriter(currentFile);

如果 s 包含无效字符,例如基于 Unix 的操作系统中的“/”,则会(正确地)抛出 java.io.FileNotFoundException。

我怎样才能安全地编码字符串,以便它可以用作文件名?

编辑:我希望的是为我执行此操作的 API 调用。

我可以做这个:

     String s = ... // comes from external source
    File currentFile = new File(System.getProperty("user.home"), URLEncoder.encode(s, "UTF-8"));
    PrintWriter currentWriter = new PrintWriter(currentFile);

但我不确定 URLEncoder 是否可靠用于此目的。

原文由 Steve McLeod 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 370
2 个回答

如果您希望结果类似于原始文件,SHA-1 或任何其他哈希方案都不是答案。如果必须避免冲突,那么简单地替换或删除“坏”字符也不是答案。

相反,你想要这样的东西。 (注意:这应该被视为一个说明性的例子,而不是复制和粘贴的东西。)

 char fileSep = '/'; // ... or do this portably.
char escape = '%'; // ... or some other legal char.
String s = ...
int len = s.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
    char ch = s.charAt(i);
    if (ch < ' ' || ch >= 0x7F || ch == fileSep || ... // add other illegal chars
        || (ch == '.' && i == 0) // we don't want to collide with "." or ".."!
        || ch == escape) {
        sb.append(escape);
        if (ch < 0x10) {
            sb.append('0');
        }
        sb.append(Integer.toHexString(ch));
    } else {
        sb.append(ch);
    }
}
File currentFile = new File(System.getProperty("user.home"), sb.toString());
PrintWriter currentWriter = new PrintWriter(currentFile);

该解决方案提供了一种可逆编码(无冲突),其中编码字符串在大多数情况下类似于原始字符串。我假设您使用的是 8 位字符。

URLEncoder 有效,但它的缺点是它编码了大量合法的文件名字符。

如果您想要一个不保证可逆的解决方案,那么只需删除“坏”字符而不是用转义序列替换它们。


上述编码的反向应该同样可以直接实现。

原文由 Stephen C 发布,翻译遵循 CC BY-SA 4.0 许可协议

我的建议是采用“白名单”方法,这意味着不要尝试过滤掉不良字符。而是定义什么是好的。您可以拒绝文件名或过滤它。如果你想过滤它:

 String name = s.replaceAll("\\W+", "");

它所做的是将任何 不是 数字、字母或下划线的字符替换为空字符。或者,您可以将它们替换为另一个字符(如下划线)。

问题是,如果这是一个共享目录,那么您不希望文件名冲突。即使用户存储区域被用户隔离,您也可能只是通过过滤掉坏字符而得到一个冲突的文件名。如果用户也想下载它,输入的名称通常很有用。

出于这个原因,我倾向于允许用户输入他们想要的内容,根据我自己选择的方案存储文件名(例如 userId_fileId),然后将用户的文件名存储在数据库表中。这样您就可以将其显示给用户,按照您的意愿存储内容,并且不会危及安全性或清除其他文件。

您还可以对文件进行哈希处理(例如 MD5 哈希),但是您无法列出用户输入的文件(无论如何都没有有意义的名称)。

编辑:修复了java的正则表达式

原文由 cletus 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题