在http://zhouzaibao.iteye.com/blog/346000基础上进行了修改
利用org.apache.commons.net.ftp包实现一个简单的ftp客户端实用类。主要实现一下功能
1.支持上传下载。支持断点续传
2.支持对于中文目录及中文文件创建的支持。
具体请看代码,上面有详细的注释。简化版本请参见http://zhouzaibao.iteye.com/blog/342766
枚举类UploadStatus代码:
public enum DownloadStatus {
REMOTE_FILE_NOEXIST, // 远程文件不存在
LOCAL_BIGGER_REMOTE, // 本地文件大于远程文件
DOWNLOAD_FROM_BREAK_SUCCESS, // 断点下载文件成功
DOWNLOAD_FROM_BREAK_FAILED, // 断点下载文件失败
DOWNLOAD_NEW_SUCCESS, // 全新下载文件成功
DOWNLOAD_NEW_FAILED; // 全新下载文件失败
}
枚举类DownloadStatus代码:
public enum UploadStatus {
CREATE_DIRECTORY_FAIL, // 远程服务器相应目录创建失败
CREATE_DIRECTORY_SUCCESS, // 远程服务器闯将目录成功
UPLOAD_NEW_FILE_SUCCESS, // 上传新文件成功
UPLOAD_NEW_FILE_FAILED, // 上传新文件失败
FILE_EXITS, // 文件已经存在
REMOTE_BIGGER_LOCAL, // 远程文件大于本地文件
UPLOAD_FROM_BREAK_SUCCESS, // 断点续传成功
UPLOAD_FROM_BREAK_FAILED, // 断点续传失败
DELETE_REMOTE_FAILD; // 删除远程文件失败
}
流及文件操作工具类的代码:
public class FileUtil {
/**
* 文件拷贝
*
* @param insm
* @param outsm
* @throws IOException
*/
public static void copyFile(File srcFile, File destFile) throws IOException {
if (!srcFile.exists()) {
throw new FileNotFoundException(srcFile.getName() + "不存在!");
}
if (!destFile.exists()) {
destFile.createNewFile();
}
InputStream in = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(destFile);
copyStream(in, out);
}
/**
* 流拷贝,通过对流的操作完成
*
* @param insm
* @param outsm
* @throws IOException
*/
public static void copyStream(InputStream insm, OutputStream outsm)
throws IOException {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(insm);
bos = new BufferedOutputStream(outsm);
byte[] b = new byte[8192];
int readBytes = -1;
while ((readBytes = bis.read(b)) != -1) {
bos.write(b, 0, readBytes);
}
} catch (IOException e) {
e.printStackTrace();
throw e;
} finally {
if (bis != null)
bis.close();
if (bos != null)
bos.close();
}
}
}
核心类FTPOperationProccessor代码:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import cn.com.servyou.ftpuploadtool.status.DownloadStatus;
import cn.com.servyou.ftpuploadtool.status.UploadStatus;
import cn.com.servyou.ftpuploadtool.util.FileUtil;
/**
* <p>
* Title: FTPOperationProcessor
* </p>
*
* <p>
* Description: FTP操作处理类
* </p>
*
* <p>
* Copyright: Copyright (c) 2010
* </p>
*
* @author servyou
*
* @version 1.0
*/
public class FTPOperationProcessor {
private FTPClient client = new FTPClient();
/** 默认编码 */
public final static String ENCODING = "GBK";
/** FTP传输用的编码 */
public final static String FTP_ENCODING = "ISO-8859-1";
/** 目录前缀 */
public final static String PREFIX = "/";
public FTPOperationProcessor() {
super();
}
/**
* 连接FTP服务器
*
* @param hostname
* 服务器IP,或主机名
* @param port
* 端口号
* @param username
* 用户名
* @param password
* 密码
* @return 连接成功返回true,失败返回false
* @throws IOException
*/
public boolean connect(String hostname, int port, String username,
String password) throws IOException {
int reply;
client.connect(hostname);
System.out.println("Connected to " + hostname + ".");
System.out.println(client.getReplyString());
reply = client.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
client.disconnect();
System.out.println("FTP服务器拒绝连接!");
throw new IOException("FTP服务器拒绝连接!");
} else {
if (client.login(username, password)) {
client.setListHiddenFiles(true);
return true;
}
}
return false;
}
/**
* 用户登出,并关闭连接
*
* @throws IOException
*/
public void disConnect() throws IOException {
if (client.isConnected()) {
client.logout();
client.disconnect();
}
}
/**
* 遍历服务器的某一目录
*
* @param pathname
* FTP服务器的路径,如/dir1/dir2/
* @throws IOException
*/
public void traverseDirectory(String pathname) throws IOException {
client.changeWorkingDirectory(pathname);
FTPFile[] fileList = client.listFiles(pathname);
traverse(fileList);
}
/**
* 遍历FTP服务器的目录
*
* @param ftpClient
* FTPClient
* @param fileList
* 文件列表
* @throws IOException
*/
private void traverse(FTPFile[] fileList) throws IOException {
String tempDir = null;
for (FTPFile file : fileList) {
if (file.getName().equals(".") || file.getName().equals("..")) {
continue;
}
if (file.isDirectory()) {
System.out.println("***************** Directory: " + file.getName()
+ " Start **************");
tempDir = client.printWorkingDirectory();
if (tempDir.matches("^((/\\w+))+$"))
tempDir += "/" + file.getName();
else
tempDir += file.getName();
client.changeWorkingDirectory(new String(tempDir.getBytes(ENCODING),
FTP_ENCODING));
traverse(client.listFiles(tempDir));
// 不是目录,是文件的情况
System.out.println("***************** Directory:" + file.getName()
+ " End **************\n");
} else {
System.out.println("FileName:" + file.getName() + " FileSize:"
+ file.getSize() / (1024) + "KB" + " CreateTime:"
+ file.getTimestamp().getTime());
}
}
// 遍历完当前目录,就要将工作目录改为当前目录的父目录
client.changeToParentDirectory();
}
/**
* 下载单个文件
*
* @param remote
* 远端文件
* @param local
* 本地文件
* @throws IOException
*/
public DownloadStatus download(String remote, File localFile)
throws IOException {
client.enterLocalPassiveMode();
client.setFileType(FTPClient.BINARY_FILE_TYPE);
DownloadStatus result = null;
// 检查远程文件是否存在
FTPFile[] files = client.listFiles(new String(remote.getBytes(ENCODING),
FTP_ENCODING));
if (files.length != 1) {
System.out.println("远程文件不存在");
return DownloadStatus.REMOTE_FILE_NOEXIST;
}
long lRemoteSize = files[0].getSize();
// 本地存在文件,进行断点下载
if (localFile.exists()) {
long localSize = localFile.length();
// 判断本地文件大小是否大于远程文件大小
if (localSize >= lRemoteSize) {
System.out.println("本地文件大于远程文件,下载中止");
return DownloadStatus.LOCAL_BIGGER_REMOTE;
}
// 进行断点续传,并记录状态
FileOutputStream out = new FileOutputStream(localFile, true);
client.setRestartOffset(localSize);
InputStream in = client.retrieveFileStream(new String(remote
.getBytes(ENCODING), FTP_ENCODING));
FileUtil.copyStream(in, out);
boolean isDo = client.completePendingCommand();
if (isDo) {
result = DownloadStatus.DOWNLOAD_FROM_BREAK_SUCCESS;
} else {
result = DownloadStatus.DOWNLOAD_FROM_BREAK_FAILED;
}
} else {
localFile.createNewFile();
FileOutputStream out = new FileOutputStream(localFile);
InputStream in = client.retrieveFileStream(new String(remote
.getBytes(ENCODING), FTP_ENCODING));
FileUtil.copyStream(in, out);
}
return result;
}
/**
* 上传文件到FTP服务器,支持断点续传
*
* @param local
* 本地文件名称,绝对路径
* @param remote
* 远程文件路径,使用/home/directory1/subdirectory/file.ext
* 按照Linux上的路径指定方式,支持多级目录嵌套,支持递归创建不存在的目录结构
* @return 上传结果
* @throws IOException
*/
public UploadStatus upload(String local, String remote) throws IOException {
// 设置PassiveMode传输
client.enterLocalPassiveMode();
// 设置以二进制流的方式传输
client.setFileType(FTPClient.BINARY_FILE_TYPE);
client.setControlEncoding(ENCODING);
UploadStatus result;
// 对远程目录的处理
String remoteFileName = remote;
if (remote.contains("/")) {
remoteFileName = remote.substring(remote.lastIndexOf("/") + 1);
// 创建服务器远程目录结构,创建失败直接返回
// 以下两句存在问题??
// if (createDirectory(remote) == UploadStatus.CREATE_DIRECTORY_FAIL) {
// return UploadStatus.CREATE_DIRECTORY_FAIL;
// }
}
// 检查远程是否存在文件
FTPFile[] files = client.listFiles(new String(remoteFileName
.getBytes(ENCODING), FTP_ENCODING));
if (files != null && files.length == 1) {
long remoteSize = files[0].getSize();
File f = new File(local);
long localSize = f.length();
if (remoteSize == localSize) {
return UploadStatus.FILE_EXITS;
} else if (remoteSize > localSize) {
return UploadStatus.REMOTE_BIGGER_LOCAL;
}
// 尝试移动文件内读取指针,实现断点续传
result = uploadFile(remoteFileName, f, remoteSize);
// 如果断点续传没有成功,则删除服务器上文件,重新上传
if (result == UploadStatus.UPLOAD_FROM_BREAK_FAILED) {
if (!client.deleteFile(remoteFileName)) {
return UploadStatus.DELETE_REMOTE_FAILD;
}
result = uploadFile(remoteFileName, f, 0);
}
} else {
result = uploadFile(remoteFileName, new File(local), 0);
}
return result;
}
/**
* 上传单个文件,断点续传功能
*
* @param remote
* 远程文件
* @param localFile
* 本地文件
* @throws IOException
*/
public UploadStatus uploadFile(String remote, File localFile, long remoteSize)
throws IOException {
UploadStatus status = null;
long localreadbytes = 0l;
RandomAccessFile raf = new RandomAccessFile(localFile, "r");
OutputStream out = client.appendFileStream(new String(remote
.getBytes(ENCODING), FTP_ENCODING));
// 断点续传,根据FTP服务器上文件与本地机器上文件的大小比较来判断
if (remoteSize > 0) {
client.setRestartOffset(remoteSize);
raf.seek(remoteSize);
localreadbytes = remoteSize;
}
byte[] bytes = new byte[8096];
int c;
while ((c = raf.read(bytes)) != -1) {
out.write(bytes, 0, c);
localreadbytes += c;
}
if (out != null)
out.flush();
if (raf != null)
raf.close();
if (out != null)
out.close();
boolean result = client.completePendingCommand();
if (remoteSize > 0) {
status = result ? UploadStatus.UPLOAD_FROM_BREAK_SUCCESS
: UploadStatus.UPLOAD_FROM_BREAK_FAILED;
} else {
status = result ? UploadStatus.UPLOAD_NEW_FILE_SUCCESS
: UploadStatus.UPLOAD_NEW_FILE_FAILED;
}
return status;
}
/**
* 创建目录,(远程目录格式必须是/aaa/bbb/ccc/ddd/的形式)
*
* @param remote
* 远程目录路径
* @throws IOException
*/
public UploadStatus createDirectory(String remote) throws IOException {
int start = 0, end = 0;
start = remote.startsWith("/") ? 1 : 0;
end = remote.indexOf("/", start);
for (; start < end;) {
String subDirectory = remote.substring(start, end);
if (!client.changeWorkingDirectory(new String(subDirectory
.getBytes(ENCODING), FTP_ENCODING))) {
// 目录不存在则在服务器端创建目录
if (!client.makeDirectory(new String(subDirectory.getBytes(ENCODING),
FTP_ENCODING))) {
return UploadStatus.CREATE_DIRECTORY_FAIL;
} else {
client.changeWorkingDirectory(new String(subDirectory
.getBytes(ENCODING), FTP_ENCODING));
}
}
start = end + 1;
end = remote.indexOf("/", start);
}
return UploadStatus.CREATE_DIRECTORY_SUCCESS;
}
/**
* 递归遍历本地机器的要上传的目录,遍历的同时,在FTP服务器上创建目录
* (如果在FTP服务器上目录不存在的话),上传文件
*
* @param directory
* 本地目录
* @throws IOException
*/
private void traverseLocalDirectory(File directory) throws IOException {
File[] files = directory.listFiles();
if (files == null || files.length == 0) {
client.changeToParentDirectory();
return;
}
for (File file : files) {
if (file.isDirectory()) {
String directoryName = file.getName();
client.makeDirectory(new String(directoryName.getBytes(ENCODING),
FTP_ENCODING));
client.changeWorkingDirectory(new String(directoryName
.getBytes(ENCODING), FTP_ENCODING));
traverseLocalDirectory(file);
} else {
System.out.println("FileName : " + file.getName());
upload(file.getAbsolutePath(), client.printWorkingDirectory() + "/"
+ file.getName());
}
}
client.changeToParentDirectory();
}
/**
* 上传本地机器的某一目录到FTP服务器的某一路径
*
* @param remoteBasePath
* FTP服务器的一个路径
* @param localRootDirectoryPath
* 本地机器需要上传的目录路径
* @throws IOException
*/
public UploadStatus uploadDirectory(String remoteBasePath,
String localDirectoryPath) throws IOException {
if (createDirectory(remoteBasePath) == UploadStatus.CREATE_DIRECTORY_FAIL) {
return UploadStatus.CREATE_DIRECTORY_FAIL;
// remoteBasePath FTP服务器上基目录,创建成功的话
} else {
if (client.changeWorkingDirectory(new String(remoteBasePath
.getBytes(ENCODING), FTP_ENCODING))) {
File localDirectory = new File(localDirectoryPath);
traverseLocalDirectory(localDirectory);
return UploadStatus.CREATE_DIRECTORY_SUCCESS;
} else {
return UploadStatus.CREATE_DIRECTORY_FAIL;
}
}
}
}
使用方法简介:
1.需要创建目录:
FTPOperationProcessor processor = new FTPOperationProcessor();
try {
boolean flag = processor.connect("192.168.30.190", 21, "anonymous", "");
if (flag == false) {
System.out.println("连接服务器失败");
processor.disConnect();
} else {
processor.createDirectory("/中1/中文的/信息/还是中文的/目录是中文的/xx林寺xxa11/");
}
} catch (IOException e) {
e.printStackTrace();
}
则可在FTP服务器的根目录生成目录
/中1/中文的/信息/还是中文的/目录是中文的/xx林寺xxa11
2.上传整个目录到FTP服务器的某一目录:
processor.uploadDirectory("/Jay Chang/Study/Example/", "D:\\My Documents\\Study\\Example\\");
3.下载某一文件到本地:
processor.download("/backup/sys.gho", "f:\\sys.gho");