- Jar包:apache的commons-net包;
- 支持断点续传
- 支持进度监控(有时出不来,搞不清原因)
相关知识点
- 编码格式: UTF-8等;
- 文件类型: 包括[BINARY_FILE_TYPE(常用)]和[ASCII_FILE_TYPE]两种;
- 数据连接模式:一般使用LocalPassiveMode模式,因为大部分客户端都在防火墙后面;
1. LocalPassiveMode:服务器端打开数据端口,进行数据传输;
2. LocalActiveMode:客户端打开数据端口,进行数据传输;
- 系统类型:UNIX/WINDOWS等,默认为Unix
流程
- 步骤1: 创建FTPClient对象,设置ftpClient属性:如编码格式、连接超时、文件上传下载进度监听器等;
- 步骤2: 使用ftpClient连接远程server:connect();
- 步骤3: 获取connect()的返回码getReplyCode(),判断是否连接成功:isPositiveCompletion();
- 步骤4: 登录远程server:login(),并转到相应目录,必要时要递归创建目录;
- 步骤5: 设置ftpClient属性:如缓存大小、文件类型、超时时间、数据连接模式等;
- 步骤6: ftp相关操作:如文件上传、下载等;
- 步骤7: 断开连接,释放资源:logout()/disconnect();
程序
FTP连接和登录
文件上传
文件下载
测试程序
完整程序
package com.sssppp.Communication;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.CopyStreamListener;
/**
* FTP进行文件上传和下载;
* 支持断点续传;
*/
public final class FTPUtil {
private final FTPClient ftp = new FTPClient();
/**
*
* @param hostname
* 如:IP
* @param port
* @param username
* @param password
* @return
* @throws IOException
*/
public boolean connect(String hostname, int port, String username,
String password) throws IOException {
boolean debug = false;
if (debug) {
// 设置将过程中使用到的命令输出到控制台
this.ftp.addProtocolCommandListener(new PrintCommandListener(
new PrintWriter(System.out), true));
}
//设置系统类型
final FTPClientConfig config = new FTPClientConfig(
FTPClientConfig.SYST_UNIX);
this.ftp.configure(config);
try {
this.ftp.connect(hostname, port);
if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
this.ftp.disconnect();
System.err.println("FTP server refused connection.");
return false;
}
} catch (IOException e) {
if (this.ftp.isConnected()) {
try {
this.ftp.disconnect();
} catch (IOException f) {
}
}
System.err.println("Could not connect to server.");
e.printStackTrace();
return false;
}
if (!this.ftp.login(username, password)) {
this.ftp.logout();
System.err.println("Could not login to server.");
return false;
}
return true;
}
public void disconnect() throws IOException {
if (this.ftp.isConnected()) {
try {
this.ftp.logout();
this.ftp.disconnect();
} catch (IOException f) {
}
}
}
/**
*
* @param absSrcFileName
* @param destDir
* @param destFileName
* @throws IOException
*/
public void upLoadByFtp(String absSrcFileName, String destDir,
String destFileName) throws IOException {
// 创建并转到工作目录
String absDstDir = this.ftp.printWorkingDirectory() + "/" + destDir;
absDstDir = absDstDir.replaceAll("//", "/");
createDirectory(absDstDir, this.ftp);
// 设置各种属性
this.ftp.setFileType(FTP.BINARY_FILE_TYPE);
// Use passive mode as default because most of us are behind firewalls these days.
this.ftp.enterLocalPassiveMode();
this.ftp.setControlEncoding("utf-8");
this.ftp.setBufferSize(1024);
// 进度监听
File srcFile = new File(absSrcFileName);
this.ftp.setCopyStreamListener(new MyCopyStreamListener(srcFile.length()));
FTPFile[] files = this.ftp.listFiles(destFileName);
if (files.length == 1) {// 断点续传
long dstFileSize = files[0].getSize();
if (srcFile.length() <= dstFileSize) {// 文件已存在
return;
}
boolean b = uploadFile(destFileName, srcFile, this.ftp, dstFileSize);
if (!b) {// 如果断点续传没有成功,则删除服务器上文件,重新上传
if (this.ftp.deleteFile(destFileName)) {
uploadFile(destFileName, srcFile, this.ftp, 0);
}else {
System.err.println("Delete file fail.");
}
}
} else {
uploadFile(destFileName, srcFile, this.ftp, 0);
}
}
/**
*
* @param remoteFileName
* @param localFileName
* @throws IOException
*/
public void downLoadByFtp(String remoteFileName, String localFileName)
throws IOException {
InputStream input = null;
FileOutputStream fos = null;
// 设置各种属性
this.ftp.setBufferSize(1024);
this.ftp.setDataTimeout(1000 * 10);
this.ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
this.ftp.enterLocalPassiveMode();
// 判断远程文件是否存在
FTPFile[] files = this.ftp.listFiles(remoteFileName);
if (files.length != 1) {
System.err.println("Remote file not exist.");
return;
}
//进度监听
long remoteSize = files[0].getSize();
this.ftp.setCopyStreamListener(new MyCopyStreamListener(remoteSize));
File file = new File(localFileName);
if (file.exists()) {
long localSize = file.length();
if (localSize >= remoteSize) {
return;
}
System.out.println("@@@Break point download.@@@");
fos = new FileOutputStream(file, true);// append模式
this.ftp.setRestartOffset(localSize);
} else {
fos = new FileOutputStream(file); // override模式
}
input = this.ftp.retrieveFileStream(remoteFileName);
byte[] b = new byte[8192];
int n = 0;
while (-1 != (n = input.read(b))) {
if (Thread.currentThread().isInterrupted()) {
break;
}
fos.write(b, 0, n);
}
if (input != null) {
input.close();
}
if (fos != null) {
fos.flush();
fos.close();
}
if (!this.ftp.completePendingCommand()) {
System.err.println("Download file fail.");
this.ftp.logout();
this.ftp.disconnect();
}
}
/**
*
* @param destFileName
* @param srcFile
* @param ftpClient
* @param dstFileSize 文件写入的起始位置; >0:表示断点续传,<=0:表示上传新文件
* @return
* @throws IOException
*/
private boolean uploadFile(String destFileName, File srcFile,
FTPClient ftpClient, long dstFileSize) throws IOException {
RandomAccessFile input = null;
OutputStream fout = null;
input = new RandomAccessFile(srcFile, "r"); // 只读模式
if (dstFileSize > 0) {// 断点续传
fout = ftpClient.appendFileStream(destFileName);
input.seek(dstFileSize);
ftpClient.setRestartOffset(dstFileSize);
} else {
fout = ftpClient.storeFileStream(destFileName);
}
byte[] b = new byte[8192]; // 缓存大小
int n = 0;
while (-1 != (n = input.read(b))) {
if (Thread.currentThread().isInterrupted()) {
break;
}
fout.write(b, 0, n);
}
if (input != null) {
input.close();
}
if (fout != null) {
fout.flush();
fout.close();
}
if (!ftpClient.completePendingCommand()) {
System.err.println("Upload file fail.");
ftpClient.logout();
ftpClient.disconnect();
return false;
}
return true;
}
/**
* 在FTP服务器上创建并转到工作目录
*
* @param relativePath
* 相对工作路径,不包含文件名:如 dd/11/22/33
* @param ftpClient
* 录创建是否成功
* @return
* @throws IOException
*/
private boolean createDirectory(String relativePath, FTPClient ftpClient)
throws IOException {
if (!relativePath.startsWith("/")) {
relativePath = "/" + relativePath;
}
String dir = (ftpClient.printWorkingDirectory().equals("/") ? ""
: ftpClient.printWorkingDirectory()) + relativePath;
if (!ftpClient.changeWorkingDirectory(dir)) {
//目录不存在,则创建各级目录
for (String subDir : relativePath.split("/")) {
if (!subDir.equals("")) {
String newDir = ftpClient.printWorkingDirectory() + "/"
+ subDir;
ftpClient.mkd(newDir);
if (!ftpClient.changeWorkingDirectory(newDir)) {
return false;
}
}
}
}
return true;
}
/**
* 进度监听器
*/
private class MyCopyStreamListener implements CopyStreamListener {
private long totalSize = 0;
private long percent = -1; // 进度
/**
* 文件的总大小
* @param totalSize
*/
public MyCopyStreamListener(long totalSize) {
super();
this.totalSize = totalSize;
}
@Override
public void bytesTransferred(CopyStreamEvent event) {
bytesTransferred(event.getTotalBytesTransferred(),
event.getBytesTransferred(), event.getStreamSize());
}
//totalBytesTransferred:当前总共已传输字节数;
//bytesTransferred:最近一次传输字节数
@Override
public void bytesTransferred(long totalBytesTransferred,
int bytesTransferred, long streamSize) {
if (percent >= totalBytesTransferred * 100 / totalSize) {
return;
}
percent = totalBytesTransferred * 100 / totalSize;
System.out.println("Completed " + totalBytesTransferred + "("
+ percent + "%) out of " + totalSize + ".");
}
}
public static void main(String[] args) throws IOException {
String hostname = "10.180.137.241";
String username = "xxx";
String password = "xxx";
int port = 21;
FTPUtil ftp = new FTPUtil();
//上传文件
String absSrcFileName = "C:\\tmp\\m2eclipse1.zip";
String destDir = "ww/11/22/33";
String destFileName = "m2eclipse1.zip";
ftp.connect(hostname, port, username, password);
ftp.upLoadByFtp(absSrcFileName, destDir, destFileName);
ftp.disconnect();
// 下载文件
String localFileName = "C:\\tmp\\m2eclipse-download3333.zip";
String remoteFileName = "/ww/11/22/33/m2eclipse.zip";
ftp.connect(hostname, port, username, password);
ftp.downLoadByFtp(remoteFileName, localFileName);
ftp.disconnect();
}
}
参考链接
https://my.oschina.net/csmw00/blog/676049 (可参考如何实现进度监控)