public classFtpClientUtil {privateString host;private intport;privateString username;privateString password;private int bufferSize = 10 * 1024 * 1024;private int soTimeout = 15000;privateFTPClient ftp;publicFTPClient getFtp() {returnftp;
}public voidsetFtp(FTPClient ftp) {this.ftp =ftp;
}privateUploadStatus uploadStatus;publicUploadStatus getUploadStatus() {returnuploadStatus;
}public voidsetUploadStatus(UploadStatus uploadStatus) {this.uploadStatus =uploadStatus;
}public static classBuilder {privateString host;private int port = 21;privateString username;privateString password;private int bufferSize = 1024 * 1024;privateFTPClientConfig config;private int defaultTimeout = 15000;private int connectTimeout = 15000;private int dataTimeout = 15000;private int controlKeepAliveTimeout = 300;private int soTimeout = 15000;publicBuilder() {
}publicBuilder host(String host) {this.host =host;return this;
}public Builder port(intport) {this.port =port;return this;
}publicBuilder username(String username) {this.username =username;return this;
}publicBuilder password(String password) {this.password =password;return this;
}public Builder bufferSize(intbufferSize) {this.bufferSize =bufferSize;return this;
}publicBuilder config(FTPClientConfig config) {this.config =config;return this;
}public Builder defaultTimeout(intdefaultTimeout) {this.defaultTimeout =defaultTimeout;return this;
}public Builder connectTimeout(intconnectTimeout) {this.connectTimeout =connectTimeout;return this;
}public Builder dataTimeout(intdataTimeout) {this.dataTimeout =dataTimeout;return this;
}public Builder soTimeout(intsoTimeout) {this.soTimeout =soTimeout;return this;
}public Builder controlKeepAliveTimeout(intcontrolKeepAliveTimeout) {this.controlKeepAliveTimeout =controlKeepAliveTimeout;return this;
}public FtpClientUtil build() throwsIOException {
FtpClientUtil instance= new FtpClientUtil(this.host, this.port, this.username, this.password,this.bufferSize, this.config, this.defaultTimeout, this.dataTimeout, this.connectTimeout,this.controlKeepAliveTimeout, this.soTimeout);returninstance;
}
}private FtpClientUtil(String host, int port, String username, String password, intbufferSize,
FTPClientConfig config,int defaultTimeout, int dataTimeout, intconnectTimeout,int controlKeepAliveTimeout, int soTimeout) throwsIOException {this.host =host;this.port =port;this.username =username;this.password =password;this.bufferSize =bufferSize;this.soTimeout =soTimeout;this.ftp = newFTPClient();if (config != null) {this.ftp.configure(config);
}
ftp.setControlEncoding("UTF-8");//ftp.setControlEncoding("GBK");//ftp.setControlEncoding("gb2312");
ftp.enterLocalPassiveMode();
ftp.setDefaultTimeout(defaultTimeout);
ftp.setConnectTimeout(connectTimeout);
ftp.setDataTimeout(dataTimeout);//ftp.setSendDataSocketBufferSize(1024 * 256);
if (this.bufferSize > 0) {
ftp.setBufferSize(this.bufferSize);
}//keeping the control connection alive
ftp.setControlKeepAliveTimeout(controlKeepAliveTimeout);//每大约5分钟发一次noop,防止大文件传输导致的控制连接中断
}public FtpClientUtil connect() throwsSocketException, IOException {if (!this.ftp.isConnected()) {this.ftp.connect(this.host, this.port);int reply = this.ftp.getReplyCode();if (!FTPReply.isPositiveCompletion(reply)) {
logger.warn("ftp服务器返回码[{}], 连接失败...", reply);throw new IllegalStateException("连接ftp服务器失败,返回的状态码是" +reply);
}
}this.ftp.setSoTimeout(this.soTimeout);return this;
}public FtpClientUtil login() throwsIOException {boolean suc = this.ftp.login(this.username, this.password);if (!suc) {throw new IllegalStateException("登录ftp服务器失败");
}return this;
}/*** ftp上传文件功能
*
*@paramfile
* 要上传的文件
*@paramrelativePath
* 要上传到ftp服务器的相对路径
*@return*@throwsIOException*/
public FtpClientUtil upload(File file, String relativePath) throwsIOException {
FileInputStream fInputStream= newFileInputStream(file);return this.upload(fInputStream, file.getName(), relativePath, file.length());
}public FtpClientUtil upload(InputStream inputStream, String name, String relativePath, longlocalSize)throwsIOException {
ftp.setFileType(FTP.BINARY_FILE_TYPE);
changeWorkingDirectory(relativePath);this.ftp.enterLocalPassiveMode();
FTPFile[] listFiles= this.ftp.listFiles(name);//long localSize = inputStream.available();//? 不知道好用否
if (listFiles.length == 1) {long remoteSize = listFiles[0].getSize();if (remoteSize ==localSize) {this.setUploadStatus(UploadStatus.File_Exits);return this;
}else if (remoteSize >localSize) {this.setUploadStatus(UploadStatus.Remote_Bigger_Local);return this;
}this.uploadFile(inputStream, name, remoteSize, localSize);
}else{this.uploadFile(inputStream, name, 0, localSize);
}
logger.info("{}/{} upload success", relativePath, name);return this;
}private void uploadFile(InputStream inputStream, String name, long remoteSize, long localSize) throwsIOException {this.ftp.enterLocalPassiveMode();
OutputStream output= null;long step = localSize / 100;long process = 0;long localreadbytes = 0L;try{if (remoteSize > 0) {
output= this.ftp.appendFileStream(name);this.ftp.setRestartOffset(remoteSize);
inputStream.skip(remoteSize);
process= remoteSize /step;
localreadbytes=remoteSize;
}else{
output= this.ftp.storeFileStream(name);
}byte[] bytes = new byte[1024];intc;while ((c = inputStream.read(bytes)) != -1) {
output.write(bytes,0, c);
localreadbytes+=c;if (localreadbytes / step >= process + 10) {
process= localreadbytes /step;
logger.info("文件【" + name + "】上传ftp进度汇报, process = " +process);
}
}
logger.info("文件" + name + "上传ftp进度汇报, process = " + 100);
output.flush();
inputStream.close();
output.close();boolean result = this.ftp.completePendingCommand();if (remoteSize > 0) {this.setUploadStatus(
result?UploadStatus.Upload_From_Break_Success : UploadStatus.Upload_From_Break_Failed);
}else{this.setUploadStatus(
result?UploadStatus.Upload_New_File_Success : UploadStatus.Upload_New_File_Failed);
}
}catch(Exception e) {this.setUploadStatus(
remoteSize> 0 ?UploadStatus.Upload_From_Break_Failed : UploadStatus.Upload_New_File_Failed);
}
}public OutputStream upload(String name, String relativePath) throwsIOException {
ftp.setFileType(FTP.BINARY_FILE_TYPE);
changeWorkingDirectory(relativePath);
ftp.enterLocalPassiveMode();return this.ftp.storeFileStream(name);
}public void changeWorkingDirectory(String relativePath) throwsIOException {if (relativePath == null) {throw new NullPointerException("relativePath can't be null");
}
String[] dirs= relativePath.split("/");for(String dir : dirs) {if (!this.ftp.changeWorkingDirectory(dir)) {if (this.ftp.makeDirectory(dir)) {this.ftp.changeWorkingDirectory(dir);
}else{
logger.warn("{}目录创建失败, 导致不能进入合适的目录进行上传", dir);
}
}
}
}/*** ftp上传目录下所有文件的功能
*
*@paramfile
* 要上传的目录
*@paramrelativePath
* 要上传到ftp服务器的相对路径
*@return*@throwsIOException*/
public FtpClientUtil uploadDir(File file, String relativePath) throwsIOException {if (!file.isDirectory()) {throw new IllegalArgumentException("file argument is not a directory!");
}
relativePath= relativePath + "/" +file.getName();
File[] listFiles=file.listFiles();for(File f : listFiles) {this.uploadFree(f, relativePath);
}return this;
}/*** ftp上传文件, 调用方不用区分文件是否为目录,由该方法自己区分处理
*
*@paramfile
* 要上传的文件
*@paramrelativePath
* 要上传到ftp服务器的相对路径
*@return*@throwsIOException*/
public FtpClientUtil uploadFree(File file, String relativePath) throwsIOException {if(file.isDirectory()) {this.uploadDir(file, relativePath);
}else{this.upload(file, relativePath);
}return this;
}/*** 本方法是上传的快捷方法,方法中自身包含了ftp 连接、登陆、上传、退出、断开各个步骤
*
*@paramfile
* 要上传的文件
*@paramrelativePath
* 要上传到ftp服务器的相对路径*/
public booleanuploadOneStep(File file, String relativePath) {try{this.connect().login().uploadFree(file, relativePath);return true;
}catch(IOException e) {
String msg= String.format("ftp上传时发生异常, filename = [%s], relativePath = [%s]", file.getName(),
relativePath);
logger.error(msg, e);return false;
}finally{this.disconnectFinally();
}
}public boolean uploadOneStepForStream(InputStream inputStram, String name, String relativePath, longlocalSize) {try{this.connect().login().upload(inputStram, name, relativePath, localSize);return true;
}catch(IOException e) {
String msg= String.format("ftp上传时发生异常, filename = [%s], relativePath = [%s]", name, relativePath);
logger.error(msg, e);return false;
}finally{this.disconnectFinally();
}
}public interfaceOutputStreamForUpload {public void write(OutputStream outputStream) throwsIOException;
}public booleanuploadOneStepForStream(OutputStreamForUpload outputUpload, String name, String relativePath) {try{this.connect().login();
OutputStream upload= this.upload(name, relativePath);
outputUpload.write(upload);return true;
}catch(IOException e) {
String msg= String.format("ftp上传时发生异常, filename = [%s], relativePath = [%s]", name, relativePath);
logger.error(msg, e);return false;
}finally{this.disconnectFinally();
}
}public FtpClientUtil logout() throwsIOException {this.ftp.logout();return this;
}public voiddisconnect() {this.disconnectFinally();
}private voiddisconnectFinally() {if (this.ftp.isConnected()) {try{this.ftp.disconnect();
}catch(IOException ioe) {
logger.warn("ftp断开服务器链接异常", ioe);
}
}
}
@OverridepublicString toString() {return "FtpClientHelper [host=" + host + ", port=" + port + ", username=" + username + ", password=" +password+ "]";
}
}