一、问题回顾
在上传文件到FTP服务器时,使用英文名都是能够正常上传文件的,但是文件名称带中文时会服务器会返回如下错误信息:
The filename, directory name, or volume label syntax is incorrect.
二、代码分析
经过调试发现,下面这句上传文件的代码返回的是false:
boolean storeFile = ftpClient.storeFile(String remote, InputStream local)
根据各种百度出来的解释是:FTP协议里面,规定文件名编码为iso-8859-1,所以目录名或文件名需要转码。
三、解决方法
首先需要设置FTP客户端编码为UTF-8
ftpClient.setControlEncoding(“UTF-8”);
其次,把remote以现在的编码,也就是GBK编码转成byte数字,然后把byte数组转成 iso-8859-1编码即可
remote = new String(remote.getBytes(“GBK”),“iso-8859-1”);
boolean storeFile = ftpClient.storeFile(remote, local)
这样就确实可以传上去,传上去的文件的名称也是对的,但是需要注意的是当你下载、获取文件信息时,你需要对文件名进行转码:
// 获取文件列表或者文件时自己反转一下文件名称编码,使之不乱码
FTPFile[] listFiles = ftpClient.listFiles(path);
for (FTPFile ftpFile : listFiles) {
byte[] bytes = ftpFile.getName().getBytes("iso-8859-1");
ftpFile.setName(new String(bytes, "GBK"));
}
// 获取文件列表或者文件时自己反转一下文件名称编码,使之不乱吗
byte[] bytes = path.getBytes("GBK");
InputStream fileStream = ftpClient.retrieveFileStream(new String(bytes, "iso-8859-1"));
log.info("文件 {} 下载成功!", path);
四、附代码
FTP相关工具类代码,主要如下:
/**
* 获取FTP客户端链接带账号和端口
*
* @param ftpHost
* @param ftpUserName
* @param ftpPassword
* @param ftpPort
* @throws IOException
*/
public static FTPClient getFTPClient(String ftpHost, int ftpPort, String ftpUserName,
String ftpPassword) {
FTPClient ftpClient = null;
try {
ftpClient = new FTPClient();
ftpClient.connect(ftpHost, ftpPort);// 连接FTP服务器
ftpClient.login(ftpUserName, ftpPassword);// 登陆FTP服务器
if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
logger.error("未连接到FTP,用户名或密码错误");
ftpClient.disconnect();
} else {
logger.info("FTP连接成功");
}
ftpClient.setControlEncoding("UTF-8"); // 中文支持
ftpClient.enterLocalPassiveMode();
//指定文件类型
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
} catch (SocketException e) {
logger.error("FTP的IP地址可能错误");
} catch (IOException e) {
logger.error("FTP的端口错误");
}
return ftpClient;
}
/*
* 上传文件到FTP服务器
*
* @param ftpHost FTP IP地址
* @param ftpPort
* @param fileUrl
* @param fileName
* @param dir
*/
public static void upload(String ftpHost, int ftpPort, String ftpUserName, String ftpPassword,
String fileUrl, String fileName, String dir) {
//1、连接ftp服务器
FTPClient ftpClient = getFTPClient(ftpHost, ftpPort, ftpUserName, ftpPassword);
URL url;
InputStream is = null;
try {
//2、获取文件
url = new URL(fileUrl);
HttpURLConnection urlcon;
urlcon = (HttpURLConnection) url.openConnection();
urlcon.setConnectTimeout(3000);
urlcon.setReadTimeout(3000);
urlcon.connect(); // 获取连接
is = urlcon.getInputStream();
//1)指定上传目录
ftpClient.changeWorkingDirectory(dir);
//2)指定文件类型
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//第一个参数:文件在远程服务器的名称
//第二个参数:文件流
boolean flag = ftpClient.storeFile(fileName, is);
if (flag) {
logger.info("上传成功");
} else {
logger.error("上传失败");
}
} catch (Exception e) {
logger.error(" url:{} connection exception", fileUrl);
} finally {
try {
ftpClient.logout();
} catch (IOException e) {
logger.error("关闭文件流异常");
}
}
}
/*
* 从FTP服务器获取文件流
*
* @param ftpHost FTP IP地址
* @param ftpUserName FTP 用户名
* @param ftpPassword FTP用户名密码
* @param ftpPort FTP端口
* @param ftpPath FTP服务器中文件所在路径 格式: ftptest/aa
* @param localPath 下载到本地的位置 格式:H:/download
* @param fileName 文件名称
*/
public static InputStream getFtpInputstream(String ftpHost, String ftpUserName,
String ftpPassword, int ftpPort, String ftpPath,
String fileName) {
FTPClient ftpClient = null;
InputStream in = null;
try {
ftpClient = getFTPClient(ftpHost, ftpPort, ftpUserName, ftpPassword);
ftpClient.changeWorkingDirectory(ftpPath);
ftpClient.setBufferSize(200 * 1024 * 1024);
in = ftpClient.retrieveFileStream(fileName);
return in;
} catch (FileNotFoundException e) {
logger.error("没有找到" + ftpPath + "文件");
} catch (SocketException e) {
logger.error("连接FTP失败");
} catch (IOException e) {
logger.error("文件读取错误。");
} finally {
try {
ftpClient.logout();
} catch (IOException e) {
logger.error("关闭文件流异常");
}
}
return null;
}