linux中ftp中文名乱码问题

leodengzx 2020-01-04

问题触发环境
1. java中使用org.apache.commons.net.ftp.FTPClient包
2. 通过chrome浏览器的file标签上传文件
3. 在windows上部署的FileZilla服务上传的文件名正常显示,在linux上的vsftpd服务文件名显示乱码
4. 直接chrome浏览器访问linux的ftp目录(chrome的默认编码是UTF-8),正常显示
5. 乱码出现后,尝试了各种方式编码处理,造成了环境的各种不可追溯
解决过程
1. 查询资料:FTP协议规定文件名编码为iso-8859-1,所以上传的文件目录或文件名需要转码
2. 参考FileZilla的流程,会向FTP服务器发送OPTS UTF8 ON命令,开启服务器对UTF-8的支持。
3. 仿照FileZilla的方式,可向服务器发送该指令,如果服务器支持UTF-8则使用UTF-8,否则使用本地编码(GBK)处理中文名
4. 起作用的java关键代码
private static String LOCAL_CHARSET = "GBK";

// FTP协议中规定的文件名编码为: iso-8859-1
private static String FTP_CHARSET = "ISO-8859-1";

...
if (ftpClient.login(user, pwd)) {
    if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
        // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8,否则使用本地编码(GBK)
        LOCAL_CHARSET = "UTF-8";
    }
    ftpClient.setControlEncoding(LOCAL_CHARSET);
    // 设置被动模式
    ftpClient.setLocalPassiveMode();
    ...
}

ftpClient.changeWorkingDirectory(encodingServerPath(remotePath))

// 上传的时候使用
ftpClient.storeFile(encodingServerPath(fileName), inputStream);

/**
 * 编码文件路径
 */
private static String encodingServerPath(String path) throws UnsupportedEncodingException {
    // FTP协议里面,规定文件名编码为iso-8859-1,所以目录名或文件名需要转码,replace处理文件路径
    return new String(path.replace("\\", "/").replaceAll("//", "/").getBytes(LOCAL_CHARSET), FTP_CHARSET);
}

// 下载的时候使用
ftpClient.retrieveFile(encodingServerPath(fileName), outputStream);

// 打印ftp工作目录时需要切换转码
System.out.println("当前工作目录:"+ new String(ftp.printWorkingDirectory().getBytes(FTP_CHARSET), LOCAL_CHARSET));
个人理解的转码和解码过程如下:
filePath 的原始编码: origin_charset => start
java后台: new String(filePath.getBytes(Local_CHARSET), "ISO-8859-1") => step_1
FTP Protocol: new String(filePath.getBytes("ISO-8859-1"), FTP_CHARSET) => step_2
FTP 服务器的默认编码: new String(filePath.getBytes(FTP_CHARSET), server_charset) => end
if(Local_CHARSET == FTP_CHARSET) upload正常, else upload异常(无法创建和切换目录)
if(origin_charset == server_charset) 文件名在FTP服务器上显示正常, else 文件在FTP服务器显示乱码,download异常
如果filePath的编码与上传是的编码不一致,download异常
web项目开发中出现乱码,要从传值开始分析编码,逐步排查,确保编码一致。要注意浏览器和某些方法的默认行为(场景较多,需要时查询即可,不在此处罗列)

测试后的代码托管在GitHub中,地址https://github.com/Hlingoes/file-message-server,位于application/FtpService中,供参考和提出改进建议

相关推荐