FTP工具类设计与编写中遇到的阻塞点
目前来讲JAVA中主要运用于FTP连接的主要有三类,sun原生的FtpClient类,apache.commons.net里的FTPClient类,除此以外还有加强版的SSH连接的SFTP连接,最常使用的是jcraft的Jsch,本文先注重研究apache的功能封装。
先上代码:
/**
* Created by LaoLian on 2016/9/23.
*/
public class FtpUtilApache {
private static String LOCAL_CHARSET = "GBK";
// FTP协议里面,规定文件名编码为iso-8859-1
private static String SERVER_CHARSET = "ISO-8859-1";
/**
* 日志引用
*/
static final JLogger logger = LoggerFactory.getLogger(FtpUtilApache.class);
/**
* 本地文件名
*/
private String localfilename;
/**
* 远程文件名
*/
private String remoteFileName;
private String localFile; //本地文件夹路径
private String distFolder; //目标路径
private static String userName; //FTP 登录用户名
private static String password; //FTP 登录密码
private static String ip; //FTP 服务器地址IP地址
private static int port; //FTP 端口
//private static String configFile = "E:\\test\\comftp\\ftpconfig.properties"; //配置文件的路径名
private static FTPClient ftpClient = null; //FTP 客户端代理
//时间格式化
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
//FTP状态码
public static int i = 1;
public static void main(String[] args) throws IOException {
connectServer();
setFileType(FTP.BINARY_FILE_TYPE);// 设置传输二进制文件
boolean a = true;
//下载///
new File("C:\\Users\\LaoLian\\Desktop\\aaa").mkdir();
downFile("ceshi" ,"C:\\Users\\LaoLian\\Desktop\\aaa");
上传
try {
String result= uploadManyFile( new File("C:\\Users\\LaoLian\\Desktop\\0"), "ceshi");
logger.info(result);
} catch (IOException e) {
logger.debug("批量本地文件上传失败!", e);
}
删除/
deleteDirectory("ceshi");
ftpClient.changeWorkingDirectory("");//跳转到目标路径
ftpClient.removeDirectory("ceshi");
closeConnect();// 关闭连接
}
/**
* 上传单个文件,并重命名
*
* @param localFile--本地文件路径
* @param distFolder--新的文件名,可以命名为空""
* @return true 上传成功,false 上传失败
*/
public static boolean uploadFile(File localFile, final String distFolder) throws IOException {
boolean flag = true;
InputStream input = new FileInputStream(localFile);
try {
connectServer();
if (input == null) {
System.out.println("本地文件" + localFile.getPath() + "不存在!");
}
flag = ftpClient.storeFile(GBKtoISO(localFile.getName()), input);//上传 注意乱码
if (flag) {
System.out.println("上传文件成功!");
} else {
System.out.println("上传文件失败!");
}
} catch (IOException e) {
logger.debug("本地文件上传失败!", e);
} finally {
input.close();
}
return flag;
}
/**
* 上传多个文件
*
* @param localFile,--本地文件夹路径
* @param distFolder--目标路径
* @return 上传失败的文件名
*/
public static String uploadManyFile(File localFile, String distFolder) throws IOException {
boolean flag = true;
StringBuffer strBuf = new StringBuffer();
int n = 0;
try {
connectServer();
ftpClient.makeDirectory(GBKtoISO(distFolder));
ftpClient.changeWorkingDirectory(GBKtoISO(distFolder));//跳转到目标上传路径
File fileList[] = localFile.listFiles();//读取本地文件夹结构或者文件
for (File upfile : fileList) {
if (upfile.isDirectory()) {// 文件夹中还有文件夹
uploadManyFile(upfile, upfile.getName());
} else {
flag = uploadFile(upfile, distFolder);
}
if (!flag) {
strBuf.append(upfile.getName() + "\r\n");
}
}
System.out.println(strBuf.toString());
} catch (NullPointerException e) {
logger.debug("本地文件上传失败!找不到上传文件!", e);
}
return strBuf.toString();
}
/**
* 下载文件
*
* @param remotePath --服务器上的文件名
* @param localPath --本地文件名
*/
public static void downFile(String remotePath, String localPath) {
FTPFile[] fs;
try {
ftpClient.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录
fs = ftpClient.listFiles();
for (FTPFile ff : fs) {
File localFile = new File(localPath +"\\"+ ISOtoGBK(ff.getName()));
if (ff.isDirectory()) {
localFile.mkdir();
downFile(ff.getName(), localPath +"\\"+ localFile.getName());
} else {
FileOutputStream is = new FileOutputStream(localFile);
ftpClient.retrieveFile(ff.getName(), is);
is.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 删除
*/
public static boolean deleteFile(String filename) {
boolean flag = true;
try {
connectServer();
flag = ftpClient.deleteFile(filename);
if (flag) {
System.out.println("删除文件成功!");
} else {
System.out.println("删除文件失败!");
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
return flag;
}
/**
* 删除文件
*/
public static void deleteDirectory(String pathname) {
try {
connectServer();
FTPFile[] files = ftpClient.listFiles(pathname);
if(null!=files && files.length!=0){
for (FTPFile f:files) {
ftpClient.changeWorkingDirectory(pathname);//跳转到目标路径
if(f.isDirectory()){
deleteDirectory(f.getName());
ftpClient.removeDirectory(f.getName());
}else{
deleteFile(f.getName());
}
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
* 关闭连接
*/
public static void closeConnect() {
try {
if (ftpClient != null) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置配置文件
*
* @param configFile
public static void setConfigFile(String configFile) {
FtpUtilApache.configFile = configFile;
}*/
/**
* 设置传输文件的类型[文本文件或者二进制文件]
*
* @param fileType--BINARY_FILE_TYPE、ASCII_FILE_TYPE
*/
public static void setFileType(int fileType) {
try {
connectServer();
ftpClient.setFileType(fileType);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 扩展使用
*
* @return ftpClient
*/
protected static FTPClient getFtpClient() {
connectServer();
return ftpClient;
}
/**
* 设置参数
*
*
*/
//private static void setArg(String configFile) {
private static void setArg() {
//property = new Properties();
//BufferedInputStream inBuff = null;
//try {
//File file = new File(configFile);
//inBuff = new BufferedInputStream(new FileInputStream(file));
//property.load(inBuff);
userName = "XXXXX";//property.getProperty("username");
password = "XXXXXXXXXXXXX";//property.getProperty("password");
ip = "*************"; //property.getProperty("ip");
port = 21;//Integer.parseInt(property.getProperty("port"));
//} catch (FileNotFoundException e1) {
//System.out.println("配置文件 " + configFile + " 不存在!");
//} catch (IOException e) {
// System.out.println("配置文件 " + configFile + " 无法读取!");
//}
}
/**
* 连接到服务器
*
* @return true 连接服务器成功,false 连接服务器失败
*/
public static boolean connectServer() {
boolean flag = true;
if (ftpClient == null) {
int reply;
try {
setArg();
ftpClient = new FTPClient();
ftpClient.setDefaultPort(port);
//ftpClient.configure(getFtpConfig());
ftpClient.connect(ip);
ftpClient.login(userName, password);
reply = ftpClient.getReplyCode();
ftpClient.setDataTimeout(120000);
if (!FTPReply.isPositiveCompletion(reply)) {
ftpClient.disconnect();
System.err.println("FTP server refused connection.");
// logger.debug("FTP 服务拒绝连接!");
flag = false;
}
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
ftpClient.enterLocalPassiveMode();
/*if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(
"OPTS UTF8", "ON"))) {// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
LOCAL_CHARSET = "UTF-8";
}*/
i++;
} catch (SocketException e) {
flag = false;
e.printStackTrace();
System.err.println("登录ftp服务器 " + ip + " 失败,连接超时!");
} catch (IOException e) {
flag = false;
e.printStackTrace();
System.err.println("登录ftp服务器 " + ip + " 失败,FTP服务器无法打开!");
}
}
return flag;
}
/**
* 重命名文件
*
* @param oldFileName --原文件名
* @param newFileName --新文件名
*/
public static void renameFile(String oldFileName, String newFileName) {
try {
connectServer();
ftpClient.rename(oldFileName, GBKtoISO(newFileName));
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
* 设置FTP客服端的配置--一般可以不设置
*
* @return ftpConfig
*/
private static FTPClientConfig getFtpConfig() {
FTPClientConfig ftpConfig = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
ftpConfig.setServerLanguageCode(FTP.DEFAULT_CONTROL_ENCODING);
return ftpConfig;
}
/**
* 转码[def---ISO] 不同的平台需要不同的转码
*
* @param obj
* @return ""
*/
private static String GBKtoISO(Object obj) {
try {
if (obj == null)
return "";
else
return new String(obj.toString().getBytes(LOCAL_CHARSET),SERVER_CHARSET);
} catch (Exception e) {
return "";
}
}
/**
* 转码[def---ISO] 不同的平台需要不同的转码
*
* @param obj
* @return ""
*/
private static String ISOtoGBK(Object obj) {
try {
if (obj == null)
return "";
else
return new String(obj.toString().getBytes(SERVER_CHARSET),LOCAL_CHARSET);
} catch (Exception e) {
return "";
}
}
/**
* 在服务器上创建一个文件夹
*
* @param dir 文件夹名称,不能含有特殊字符,如 \ 、/ 、: 、* 、?、 "、 <、>...
*/
public static boolean makeDirectory(String dir) {
connectServer();
boolean flag = true;
try {
// System.out.println("dir=======" dir);
flag = ftpClient.makeDirectory(dir);
if (flag) {
System.out.println("make Directory " + dir + " succeed");
} else {
System.out.println("make Directory " + dir + " false");
}
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
}
在编写封装FTPClient时主要遇到几点问题
1.连接登录失败
2.上传文件名中文乱码
3.上传失败
4.下载批量文件时无法创建准确的文件路径以及递归写法设计错误
以下是对应解决办法:
对于链接登录失败问题,首先需要确认你在远程客户端情况下可以登录,具体配置和登录参考http://blog.csdn.net/a910118230303/article/details/52677919 篇一
如还是行不通则查看代码编写中时候配置了连接模式,代码支持两种配置连接信息的方法,除了写死以外可以通过资源文件进行配置。
对于上传中文乱码问题则需要注意两点,其一是ftp服务器的默认编码方式与linux系统的编码方式是不同的,比如linux系统编码方式是utf-8,而我使用的ftp服务器则是ios-8321,然而我的ide编译则是GBK,所以最终应该使用的转码策略是GBK转IOS。其二则是需要注意不同的系统上的ftp支持的字符集是不同的,参看这篇博文http://www.cnblogs.com/chenfei0801/p/3427310.html可以知道需要根据不同类型设置转码格式,关键代码如下:
if (FTPReply.isPositiveCompletion(ftpClient.sendCommand(
"OPTS UTF8", "ON"))) {// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
LOCAL_CHARSET = "UTF-8";
}
对于上传失败的问题则导致的原因有很多比如路径设置时候正确,一般来说,如果按默认设置,在连接之后建议用
ftpClient.changeWorkingDirectory("");
来重置到默认路径,注意此处的区别,在很多其他博文中的语句是
ftpClient.changeWorkingDirectory("\");
实测这是错误的会导致在上传等新建操作多一层文件名为“\”的文件夹,或者直接由于找不到该路径导致路径跳转失败,另外的话在递归取文件上传与下载时要确保路径文件夹的存在,如下载时需要保证本地文件流已经打开且文件已经生成了文件夹,否则会报路径未找到的异常。
至于批量下载的错误则是需要注意前后路径的跳转和递归的调用与结束,这在代码中已经妥善的注释了。
文章最后感谢此篇博文作者http://lavasoft.blog.51cto.com/62575/100386/,整个工具类代码是由此更改而来,节省了我不少时间,但里面有很大程度的删改,很多处实测并没有成功,却被转载了很多次,疑惑中·······