从Javaservlet将具有非ASCII文件名的上传文件写入磁盘

从Javaservlet将具有非ASCII文件名的上传文件写入磁盘,java,servlets,file-upload,character-encoding,Java,Servlets,File Upload,Character Encoding,我有一个servlet,它使用ApacheCommonsFileUpload将上传的文件写入磁盘。总的来说,这一切都很好 它使用Tomcat在Windows和Linux服务器上运行。在Windows上,它可以正确处理非ASCII文件名的文件,并正确保存文件 但是,在Linux(CentOS 6)上,当包含非ASCII字符时,文件名无法正确保存 如果您尝试了三种不同版本的写入文件。在Windows中所有操作都可以,在Linux中没有一个可以,但它们会产生不同的结果 第1版: String file

我有一个servlet,它使用ApacheCommonsFileUpload将上传的文件写入磁盘。总的来说,这一切都很好

它使用Tomcat在Windows和Linux服务器上运行。在Windows上,它可以正确处理非ASCII文件名的文件,并正确保存文件

但是,在Linux(CentOS 6)上,当包含非ASCII字符时,文件名无法正确保存

如果您尝试了三种不同版本的写入文件。在Windows中所有操作都可以,在Linux中没有一个可以,但它们会产生不同的结果

第1版:

String fileName = URLDecoder.decode(encFilename, "UTF-8");
String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);

item.write(uploadedFile);
String fileName = URLDecoder.decode(encFilename, "UTF-8");
String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);

InputStream input = item.getInputStream();                    
try {
    Files.copy(input, uploadedFile.toPath());
} catch (Exception e) {
    log.error("Error writing file to disk: " + e.getMessage());
} finally {
    input.close();
}
Part filePart = request.getPart("file");
String fileName = "";
for (String cd : filePart.getHeader("content-disposition").split(";")) {
    if (cd.trim().startsWith("filename")) {
        fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
        fileName = fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
    }
}

String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);

InputStream input = filePart.getInputStream();                    
try {
    Files.copy(input, uploadedFile.toPath());
} catch (Exception e) {
    log.error("Error writing file to disk: " + e.getMessage());
} finally {
    input.close();
}
第2版:

String fileName = URLDecoder.decode(encFilename, "UTF-8");
String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);

item.write(uploadedFile);
String fileName = URLDecoder.decode(encFilename, "UTF-8");
String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);

InputStream input = item.getInputStream();                    
try {
    Files.copy(input, uploadedFile.toPath());
} catch (Exception e) {
    log.error("Error writing file to disk: " + e.getMessage());
} finally {
    input.close();
}
Part filePart = request.getPart("file");
String fileName = "";
for (String cd : filePart.getHeader("content-disposition").split(";")) {
    if (cd.trim().startsWith("filename")) {
        fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
        fileName = fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
    }
}

String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);

InputStream input = filePart.getInputStream();                    
try {
    Files.copy(input, uploadedFile.toPath());
} catch (Exception e) {
    log.error("Error writing file to disk: " + e.getMessage());
} finally {
    input.close();
}
上传一个名为:
ª¥ª¥ª¥ª¥щфааа.txt的文件
在Linux上我得到以下结果:

版本1:一个名为:
的文件????。txt

版本2
将文件写入磁盘时出错:格式错误的输入或输入包含不可复制的字符:/tmp/ª¥ª¥ª¥ª¥ª¥ª¥ª¥ª¥txt

在使用Tomcat 7和Java 7的Windows计算机上,文件名正确地写为
ª¥ª¥ª¥ª¥ª¥ыыаа.txt

第三个版本使用来自的方法,不使用FileUpload。结果与版本2生成的结果相同

第3版:

String fileName = URLDecoder.decode(encFilename, "UTF-8");
String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);

item.write(uploadedFile);
String fileName = URLDecoder.decode(encFilename, "UTF-8");
String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);

InputStream input = item.getInputStream();                    
try {
    Files.copy(input, uploadedFile.toPath());
} catch (Exception e) {
    log.error("Error writing file to disk: " + e.getMessage());
} finally {
    input.close();
}
Part filePart = request.getPart("file");
String fileName = "";
for (String cd : filePart.getHeader("content-disposition").split(";")) {
    if (cd.trim().startsWith("filename")) {
        fileName = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
        fileName = fileName.substring(fileName.lastIndexOf('/') + 1).substring(fileName.lastIndexOf('\\') + 1); // MSIE fix.
    }
}

String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);

InputStream input = filePart.getInputStream();                    
try {
    Files.copy(input, uploadedFile.toPath());
} catch (Exception e) {
    log.error("Error writing file to disk: " + e.getMessage());
} finally {
    input.close();
}
Tomcat正在使用
-Dfile运行。encoding=UTF-8
locale
显示
LANG=en_-US.UTF-8

touch“ª¥ª¥ª¥щфааа.txt”
生成具有该名称的文件

文件内容始终正确写入。(当然,没有写入任何文件的情况除外)


我遗漏了什么或做错了什么?

我通过将
java.io.File
的所有用法转换为
java.nio.Files
java.nio.Path
解决了这个问题。因此,java.io.File api似乎有缺陷。使用它,它在Windows和Linux上都可以正常工作

// The filename is passed as a URLencoded string
String fileName = URLDecoder.decode(request.getParameter("fileName"), "UTF-8");
Path filePath = Paths.get(uploadFolder, fileName);
Part filePart = request.getPart("file");

InputStream input = filePart.getInputStream();                    
try {
    Files.copy(input, filePath);
} catch (Exception e) {
    log.error("Error writing file to disk: " + e.getMessage());
} finally {
    input.close();
}

我在处理上传文件的应用程序的其他几个部分遇到了同样的问题,在所有情况下,我都摆脱了
java.io.File
,而使用
java.nio
解决了这个问题。

为什么要查看
文件
?只需使用
Path
直接尝试并调试每个版本,然后查看这三种情况下
fileName
是什么