概述
解决 FTPClient下载中文路径和中文文件名乱码的方案
解决中文的乱码问题主要以下几点:
根据FTP服务器配置编码设置客户端的编码方式ftpClient.setControlEncoding();
在判断FTP上目录是否存在对目录进行编码;new String(remotePath.getBytes(), FTP.DEFAULT_CONTROL_ENCODING)
下载文件时,文件名和路径要进行编码否则下载乱码remoteFileName = new String(remoteFileName.getBytes(), FTP.DEFAULT_CONTROL_ENCODING)
总结:乱码的主要问题是客户端编码和FTP服务端编码不一致导致的,在使用ftpClien时进行编码
参考代码
maven的是这个引入代码包
commons-net
commons-net
3.3
连接FTP服务器
public void login() throws FTPUtilsException {
ftpClient = new org.apache.commons.net.ftp.FTPClient();
try {
try {
ftpClient.connect(this.ftpAddress, this.ftpPort);
} catch (Exception e ) {
throw new FTPException("连接FTP服务器“" + this.ftpAddress +"” 失败", e);
}
try {
ftpClient.login(this.ftpUsername, this.ftpPassword);
} catch (Exception e) {
throw new FTPException("登录FTP服务器“" + this.ftpAddress +"” 失败", e);
}
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
localCharset = CHARSET_UTF8;
}
ftpClient.setControlEncoding(localCharset);
//限制缓冲区大小
ftpClient.setBufferSize(BUFFER_SIZE);
int reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
closeConnect();
throw new FTPException("连接FTP服务器“" + this.ftpAddress + "”失败");
}
ftpClient.enterLocalPassiveMode(); // 设置被动模式,开通一个端口来传输数据
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
} catch (Exception e) {
closeConnect();
throw new FTPUtilsException("登录FTP服务器失败", e);
}
}
关闭FTP连接
public void closeConnect() {
if (ftpClient != null && ftpClient.isConnected()) {
try {
ftpClient.logout();
ftpClient.disconnect();
} catch (IOException e) {
ThrowableManager.process(e);
LOGGER.error(LogProperty.LOGTYPE_DETAIL,"关闭FTP连接败", e);
}
}
}
下载该目录下所有文件到本地
/**
* 下载该目录下所有文件到本地
*
* @param ftpPath FTP服务器上的相对路径,例如:test/123
* @param localPath 保存文件到本地的路径,例如:D:/test
* @return 成功返回true,否则返回false
*/
public Boolean downLoadDirectory(String ftpPath, String localPath) throws FTPUtilsException {
return downLoadDirectory(ftpPath, localPath, true);
}
/**
* 下载该目录下所有文件到本地
*
* @param ftpPath FTP服务器上的相对路径,例如:test/123
* @param localPath 保存文件到本地的路径,例如:D:/test 全路径
* @param isRepeatDownLoad 是否重新下载
* @return 成功返回true,否则返回false
*/
public Boolean downLoadDirectory(String ftpPath, String localPath, Boolean isRepeatDownLoad) throws FTPUtilsException {
if (ftpClient != null) {
try {
String remotePath = basePath + ftpPath;// changeEncoding(basePath + ftpPath);
LOGGER.info(LogProperty.LOGTYPE_DETAIL,"文件夹路径,NfsPath=" + localPath);
File doc = new File(localPath);
if (!doc.exists()) {
doc.mkdirs();
// FileOperation.setFullAccessPermissions(localPath);
}
// 判断FTP上目录是否存在
String tmpFtpPath = new String(remotePath.getBytes(), FTP.DEFAULT_CONTROL_ENCODING);
if (!ftpClient.changeWorkingDirectory(tmpFtpPath)) {
LOGGER.info(LogProperty.LOGTYPE_DETAIL, basePath + tmpFtpPath + DIR_NOT_EXIST);
return Boolean.FALSE;
}
FTPFile[] ftpFiles = ftpClient.listFiles(tmpFtpPath);
for(FTPFile f: ftpFiles) {
String ftpName = new String(f.getName().getBytes(),CHARSET_UTF8);
LOGGER.info(LogProperty.LOGTYPE_DETAIL,"ftp文件=" +ftpName);
if(f.isFile()) {
downloadFile(ftpName, localPath, ftpPath);
} else {
String dirFtpPath = ftpPath + '/' + ftpName;
String dirlocalPath = localPath + File.separator + ftpName;
downLoadDirectory(dirFtpPath, dirlocalPath);
}
}
} catch (Exception e) {
throw new FTPUtilsException("下载FTP目录“ "+ ftpPath +"”失败", e);
}
}
return Boolean.TRUE;
}
下载文件
/***
* 下载文件
* @param remoteFileName 待下载文件名称
* @param localDires 下载到当地那个路径下
* @param remoteDownLoadPath remoteFileName所在的路径
* */
public boolean downloadFile(String remoteFileName, String localDires,
String remoteDownLoadPath) throws FTPUtilsException {
String strFilePath = localDires + File.separator + remoteFileName;
LOGGER.info(LogProperty.LOGTYPE_DETAIL,"开始下载文件" + strFilePath);
BufferedOutputStream outStream = null;
boolean success = false;
InputStream inputStream = null;
try {
String tmpFtpPath = new String(remoteDownLoadPath.getBytes(), FTP.DEFAULT_CONTROL_ENCODING);
if (!ftpClient.changeWorkingDirectory(tmpFtpPath)) {
LOGGER.warn(LogProperty.LOGTYPE_DETAIL, basePath + tmpFtpPath + DIR_NOT_EXIST);
return Boolean.FALSE;
}
LOGGER.info(LogProperty.LOGTYPE_DETAIL ,remoteFileName + "开始下载....");
/解决中文乱码/
// String remoteFileName2 = new String(remoteFileName.getBytes(), FTP.DEFAULT_CONTROL_ENCODING);
// inputStream = ftpClient.retrieveFileStream(remoteFileName2);
// copyInputStreamToFile(inputStream, FileUtils.getFile(strFilePath));
// inputStream.close();
// ftpClient.completePendingCommand();
/中文乱码/
outStream = new BufferedOutputStream(new FileOutputStream(strFilePath));
// 解决中文乱码
remoteFileName = new String(remoteFileName.getBytes(), FTP.DEFAULT_CONTROL_ENCODING);
success = ftpClient.retrieveFile(remoteFileName, outStream);
/中文乱码/
if (success) {
LOGGER.info(LogProperty.LOGTYPE_DETAIL, remoteFileName + "成功下载到" + strFilePath);
return success;
}
}catch (Exception e) {
LOGGER.error(LogProperty.LOGTYPE_DETAIL, remoteFileName + "成功下载失败“" + strFilePath +"”", e);
throw new FTPUtilsException(remoteFileName + "下载失败");
}finally {
if(inputStream != null) {
try {
inputStream.close();
} catch (IOException ex) {
ThrowableManager.process(ex);
}
}
if (null != outStream) {
try {
outStream.flush();
outStream.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
return success;
}
检查FTP目录是否存在及下面是否存在文件
/**
* 检查FTP目录是否存在及下面是否存在文件
*
* @param ftpPath FTP服务器文件相对路径,例如:test/123
* @return void
*/
public void checkDirFtp(String ftpPath) throws FTPUtilsException {
String path = changeEncoding(basePath + ftpPath);
if (ftpClient != null) {
try {
// 判断是否存在该目录
if (!ftpClient.changeWorkingDirectory(path.length() == 0 ? "/" : path)) {
throw new FTPUtilsException("FTP上不不存在此目录 “" + path + "”");
}
} catch (IOException e ) {
throw new FTPUtilsException("FTP上不不存在此目录 “" + path + "”");
}
try {
FTPFile[] ftpFiles = ftpClient.listFiles();
if (ftpFiles.length == 0) {
throw new FTPUtilsException("FTP目录 “ "+ftpPath+" ” 下文件为空,禁止操作!");
}
} catch (IOException e ) {
throw new FTPUtilsException("FTP上不不存在此目录,“" + path + "”!");
}
} else {
throw new FTPUtilsException("请先登录FTP服务器!");
}
}
main
public static void main(String[] args) {
String ftpAddress = "";
int ftpPort = 2222;
String ftpuser = "";
String ftppwd = "";
String ftpPath = "";
String savPath = "D:\\Temp\\ftp";
FTPUtils ftpUtils = new FTPUtils(ftpAddress, ftpPort, ftpuser, ftppwd);
try {
ftpUtils.login();
ftpUtils.downLoadDirectory(ftpPath, savPath);
} catch (Exception e) {
e.printStackTrace();
} finally {
ftpUtils.closeConnect();
}
System.exit(-1);
}
FTPUtils 参考代码
public class FTPUtils {
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
/**
* 日志对象s
**/
private static final Log LOGGER = LogFactory.getLogger(FTPUtils.class);
/**
* 该目录不存在
*/
public static final String DIR_NOT_EXIST = "该目录不存在";
/**
* "该目录下没有文件
*/
public static final String DIR_CONTAINS_NO_FILE = "该目录下没有文件";
/**
* FTP地址
**/
private String ftpAddress;
/**
* FTP端口
**/
private int ftpPort = 521;
/**
* FTP用户名
**/
private String ftpUsername;
/**
* FTP密码
**/
private String ftpPassword;
/**
* FTP基础目录
**/
private String basePath = "";
/**
* 本地字符编码
**/
private static String localCharset = "GBK";
/**
* FTP协议里面,规定文件名编码为iso-8859-1
**/
private static String serverCharset = "ISO-8859-1";
/**
* UTF-8字符编码
**/
private static final String CHARSET_UTF8 = "UTF-8";
/**
* OPTS UTF8字符串常量
**/
private static final String OPTS_UTF8 = "OPTS UTF8";
/**
* 设置缓冲区大小4M
**/
private static final int BUFFER_SIZE = 1024 * 1024 * 4;
/**
* FTPClient对象
**/
private static org.apache.commons.net.ftp.FTPClient ftpClient = null;
public FTPUtils(String ftpAddress, int ftpPort, String ftpUsername, String ftpPassword) {
this.ftpAddress = ftpAddress;
this.ftpPort = ftpPort;
this.ftpUsername = ftpUsername;
this.ftpPassword = ftpPassword;
}
}
/**
* FTP服务器路径编码转换
*
* @param ftpPath FTP服务器路径
* @return String
*/
private static String changeEncoding(String ftpPath) {
String directory = null;
try {
if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(OPTS_UTF8, "ON"))) {
localCharset = CHARSET_UTF8;
}
directory = new String(ftpPath.getBytes(localCharset), serverCharset);
} catch (Exception e) {
LOGGER.error(LogProperty.LOGTYPE_DETAIL, "路径编码转换失败", e);
}
return directory;
}
FTP异常类
public class FTPUtilsException extends Exception {
public FTPUtilsException(String message) {
super(message);
}
public FTPUtilsException(String message, Throwable cause) {
super(message, cause);
}
}