JCIFS中某些非ASCII字符的问题

15
我正在使用JCIFS访问一个文件共享,其中有很多日语名称,当中出现“・”字符时我遇到了问题。
例如: 路径为人事部/要員・コスト管理課/,第一部分没问题,但第二部分会导致问题。这可能与“・”可以用斜杠输入有关,但我不确定。我尝试了转义字符,但似乎并没有解决问题。你有任何线索是什么原因吗?

5
请添加一些最小的代码,以便我们可以更清楚地理解。这将有助于我们给出答案。 - SkyWalker
3
需要更具体地了解出现哪些问题 - 错误消息、崩溃、损坏等? - gordy
3
我猜测:・(片假名中点)是当文件名包含非法字符时显示的字符。因此,实际上文件名中并没有・,这就是为什么您无法访问它的原因。 - heenenee
问题:1)您的用户是在尝试访问已知文件吗(您能确认这一点),因为未知的用户可能会导致错误。2)路径是否完整? - jeorfevre
当你说“第一部分”时,是指第3个符号后第一个“/”之前的位,即要在文件系统上创建与上面名称相同的目录,则需要创建<b>两个</b>目录而不是一个,例如mkdir -p 人事部/要員・コスト管理課/将起作用,但mkdir 人事部/要員・コスト管理課/则不会。 - Harry
2个回答

8

对于U+30FB (片假名中点)的更新:

正如 @sergey-tachenov 指出,这个问题与 U+30FB (片假名中点) 有关,因此需要解决。为此,我想分享一些以前的项目经验和建议。

建议-1:

在我的一个项目中,我们正在为项目制作一些手册。手册使用了多种语言。我们也遇到了同样类型的问题。我们使用了 Lotus Notes 工具。在这种情况下,我们创建了一些过滤器,将那些字符或字形替换为点。之后,Lotus Notes 创建文件夹和文件名,之后再作为路径使用。这种方式解决了那个问题。如果你有这种选项,那么你可以很容易地解决问题。

建议-2:

有很多人遇到了同样类型的问题。所以他们尝试了各种方式。

有些人说:

  • 用点(.)代替解决了此问题。
  • KATAKANA MIDDLE DOT (・) 是一个双倍宽度字符。如果您想使用平假名(日语)中点,请考虑使用 HALFWIDTH KATAKANA MIDDLE DOT。
  • 换成普通的符号就能正常工作。

如果您查看 twitter-text,他们已经为 KATAKANA MIDDLE DOT (・) 提供了解决方案。请参见 github repo

资源链接

Twitter-Text 中解决“平假名中点”问题

但 attom 开发者 chrissimpkins 表示如下:

我可以确认我们常规的Hack字体中没有片假名中间点符号(U+30FB)。有一个中间点符号(U+00B7),它的外观与您在此处需要的相同。我可以确认,U+00B7符号具有与其余常规集合(和所有其他变体集合)相同的固定宽度间距。 资源链接:https://github.com/atom/atom/issues/9115
首先,我想和你分享的是点号(.)是ASCII字符。因此,点号(.)不是问题所在。字符编码服务器设置可能是问题所在。
URL只能使用ASCII字符集通过互联网发送。如果URL包含ASCII字符集之外的字符,则必须将URL进行转换。

SMB URL如下所示:

smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?param=value[param2=value2[...]]]

jCIFS还可以访问服务器和工作组。

重要提示:表示工作组、服务器、共享或目录的所有SMB URL都需要一个斜杠“/”作为结尾。

使用java.net.URL类与'smb://'URL一起使用时,需要首先调用static jcifs.Config.registerSmbURLHandler();方法。这是必需的,以注册SMB协议处理程序。

SMB URL的userinfo组件(domain;user:pass)如果包含保留字符,则必须进行URL编码。根据RFC 2396,这些字符是非US-ASCII字符和大多数元字符,但jCIFS将与除'@'(用于将userinfo组件与服务器分隔)和'%'(URL转义字符本身)之外的任何内容正常工作。


字符检查和设置

然后您需要知道您正在使用哪种字符集。通过使用以下代码,您可以获取:

System.out.println(Charset.defaultCharset());

或者您可以给出命令

$ testparm -v | grep dos 显示Samba的默认OEM编码

CIFS使用UTF-16LE或默认代码页。JCIFS使用的默认代码页是Cp850或US_ASCII。

在jCIFS中,您可以将其设置为UTF-8并进行检查:

System.setProperty("jcifs.encoding", "UTF8");

对于日本语环境,您可以尝试

System.setProperty("jcifs.encoding", "Shift_JIS");

分享名称、密码和某些包含非 ASCII 字符的文件和目录名称可能无法正确处理。默认情况下,此属性为 Cp860,即 MS-DOS Latin1。
注意:Cp860 字符集转换器位于 jre/lib/charsets.jar 中,据我所知,只有 Sun 公司国际化版的 JRE 支持。如果没有可用的 Cp860,则会发生异常。要避免此异常,您可以将 jcifs.encoding 设置为 ASCII,但具有非 ASCII 字符的共享名称和密码将无法正确处理。要确定 jCIFS 是否正确处理这些字符,请创建一个包含非 ASCII 字符(例如 Grüße)的共享,并尝试使用 ListFiles.java 示例程序列出该共享。

使用日语设置客户端属性

对于日语,您可以尝试设置jcifs.encoding = Shift_JIS

以下表格显示J2SE 5.0支持的日语编码集。新的java.nio API使用的规范名称在许多情况下与java.io和java.lang API中使用的名称不同。

----------------------------------------------------------------------------------------------
|Canonical Name for  | Canonical Name for java.io  |               Description               |
|   java.nio API     |      and java.lang API      |                                         |
----------------------------------------------------------------------------------------------
|      EUC-JP        |           EUC_JP            | JISX 0201, 0208 and 0212, EUC encoding  | 
|                    |                             |               Japanese                  |
----------------------------------------------------------------------------------------------
|    ISO-2022-JP     |         ISO2022JP           | JIS X 0201, 0208, in ISO 2022 form,     | 
|                    |                             |               Japanese                  |
----------------------------------------------------------------------------------------------
|      Shift_JIS     |             SJIS            |            Shift-JIS, Japanese          | 
----------------------------------------------------------------------------------------------
|    windows-31j     |           MS932             |             Windows Japanese            | 
----------------------------------------------------------------------------------------------
|  x-euc-jp-linux    |        EUC_JP_LINUX         | JISX 0201, 0208, EUC encoding Japanese  | 
----------------------------------------------------------------------------------------------
|   x-eucJP-Open     |       EUC_JP_Solaris        | JISX 0201, 0208, 0212, EUC encoding     | 
|                    |                             |               Japanese                  |
----------------------------------------------------------------------------------------------
|     x-IBM33722     |           Cp33722           | IBM-eucJP - Japanese (superset of 5050) | 
----------------------------------------------------------------------------------------------
|      x-IBM930      |            Cp930            | Japanese Katakana-Kanji mixed with 4370 | 
|                    |                             |         UDC, superset of 5026           |
----------------------------------------------------------------------------------------------
|      x-IBM939      |            Cp939            | Japanese Latin Kanji mixed with 4370    | 
|                    |                             |         UDC, superset of 5035           |
----------------------------------------------------------------------------------------------
|      x-IBM942      |            Cp942            |  IBM OS/2 Japanese, superset of Cp932   | 
----------------------------------------------------------------------------------------------
|      x-IBM943      |            Cp943            | IBM OS/2 Japanese, superset of Cp932    | 
|                    |                             |         and Shift-JIS                   |
----------------------------------------------------------------------------------------------

我分享了一些JCIFS的完整代码示例。你可以试一试。

  1. 使用Java在网络共享文件夹中复制文件
  2. 使用Java在Windows网络中复制资源
  3. Java教程 - 使用JCIFS通过用户名和密码将文件复制到共享网络驱动器

这是一个检索文件的示例:

import jcifs.smb.*;

jcifs.Config.setProperty( "jcifs.netbios.wins", "192.168.1.220" );
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("domain", "username", "password");
SmbFileInputStream in = new SmbFileInputStream("smb://host/c/My Documents/人事部/要員・コスト管理課/somefile.txt", auth);
byte[] b = new byte[8192];
int n;
while(( n = in.read( b )) > 0 ) {
    System.out.write( b, 0, n );
}

你可以读/写、删除、创建目录、重命名、列出目录内容、列出网络上的工作组/ntdomains和服务器、列出服务器共享、打开命名管道、验证Web客户端等等。
SmbFile、SmbFileInputStream和SmbFileOutputStream类类似于File、FileInputStream和FileOutputStream类。
通过使用FileInputStream和FileOutputStream,代码将如下所示:
SmbFile[] files = getSMBListOfFiles(sb, logger, domain, userName, password, sourcePath, sourcePattern);

    if (files == null)
        return false;
    output(sb, logger, "      Source file count: " + files.length);
    String destFilename;
    FileOutputStream fileOutputStream;
    InputStream fileInputStream;
    byte[] buf;
    int len;
    for (SmbFile smbFile: files) {
        destFilename = destinationPath + smbFile.getName();
        output(sb, logger, "         copying " + smbFile.getName());
        try {
            fileOutputStream = new FileOutputStream(destFilename);
            fileInputStream = smbFile.getInputStream();
            buf = new byte[16 * 1024 * 1024];
            while ((len = fileInputStream.read(buf)) > 0) {
                fileOutputStream.write(buf, 0, len);
            }
            fileInputStream.close();
            fileOutputStream.close();
        } catch (SmbException e) {
            OutputHandler.output(sb, logger, "Exception during copyNetworkFilesToLocal stream to output, SMP issue: " + e.getMessage(), e);
            e.printStackTrace();
            return false;
        } catch (FileNotFoundException e) {
            OutputHandler.output(sb, logger, "Exception during copyNetworkFilesToLocal stream to output, file not found: " + e.getMessage(), e);
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            OutputHandler.output(sb, logger, "Exception during copyNetworkFilesToLocal stream to output, IO problem: " + e.getMessage(), e);
            e.printStackTrace();
            return false;
        } finally {
            OutputHandler.output(sb, logger, "Exception during copyNetworkFilesToLocal stream to output, IO problem: " + e.getMessage(), e);
            e.printStackTrace();
            return false;
        }
    }

感谢@一个名叫haney的人提供帮助。

资源链接:如何使用Java中的jcifs从smb共享复制文件到本地驱动器?


注意事项-1:

对于使用HTTPS和Tomcat的用户,需注意以下内容:

大多数情况下运行在HTTP上的URL可以正常工作,但是在使用HTTPS(即通过SSL)时却不行。这通常会导致在HTTPS URL中出现Unicode(非ASCII)字符时,URL看起来不正确,并且提供的页面包含许多错误。 这是由于在运行JIRA的Apache Tomcat应用服务器的conf/server.xml中未定义useBodyEncodingForURI="true"标志所致。此标志在'推荐'分发版的JIRA中默认设置为如此。

但是,在JIRA WAR设置中可能不是这种情况。因此,请确保在以下元素中包含useBodyEncodingForURI="true"标志,该元素位于您运行JIRA的Apache Tomcat安装的conf/server.xml文件中:

<Connector port="8443" maxHttpHeaderSize="8192"
              maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
              enableLookups="false" disableUploadTimeout="true"
              acceptCount="100" scheme="https" secure="true"
              clientAuth="false" sslProtocol="TLS" useBodyEncodingForURI="true" />

在所有连接器定义中指定useBodyEncodingForURI="true",即HTTP和HTTPS连接器,如安装JIRA on Tomcat 6.0 or 7.0文档的“修改Tomcat server.xml”部分所述。

资源链接:

如何使HTTPS URL中的Unicode“非ASCII”字符显示正确


对于非ASCII字符,您可以参考以下链接:

  1. (请)停止在URL中使用不安全字符
  2. 我可以在URL中使用非ASCII字符吗?
  3. 在URL中使用非ASCII字符是否明智?

1
那个点肯定不是ASCII字符。它是U+30FB(片假名中间点)。 - Sergei Tachenov
@SergeyTachenov 感谢您指出。我已经更新了答案并提出了一些建议。 - SkyWalker

4

请看heenenee的评论,浏览您的服务器文件系统以检查真实共享名称。我使用Java源代码(UTF-8)在Samba服务器(UTF-8)上测试访问带有中间点和日语名称的网络共享,并没有遇到问题。

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Test;

import jcifs.smb.SmbFile;
import junit.framework.TestCase;

public class JCifstest extends TestCase {

    @Test
    public void testJCifs() throws IOException {

        System.out.println(Charset.defaultCharset());

        SmbFile smbFile = new SmbFile("smb://myuser:mypass@myserver/basepath/人事部要員・コスト管理課/test.txt");
        File destFile = new File("/tmp/" + smbFile.getName());
        FileUtils.writeByteArrayToFile(destFile, IOUtils.toByteArray(smbFile.getInputStream()));
        assertEquals("content", FileUtils.readFileToString(destFile));
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接