介绍:
FTPClient是一个强大的FTP上传下载工具,可以实现各种方式的ftp文件传输,可以支持上传下载各种大文件(已在实践中使用),而且存在官网使用户方便的使用这个工具等等。
1.首先,程序中设置ftp请求方式为被动模式,即程序去请求ftp服务器,要求服务器来打开一个端口,让客户端传输文件。这是基本的,但是无法避免上传阻塞问题。
2.其次,设置连接超时,数据传输超时等等,也无法避免阻塞。
3.接着,调用上传或下载后,调用stream.close()方法,同样无法避免阻塞,这是基本的操作,说明不了什么。
4.最后,我在程序中加入了上传下载listener(ftpclient自己实现的一个监听器),只是为了显示上传下载进度。
结论:1.这些都算基本的功能,但是程序如果长时间上传或下载一个大文件,或者长时间多任务上传多个任务,还是会出现阻塞现象。
解决方式:我使用的解决方案是,去掉监听器,然后重新运行,程序竟然不再阻塞!具体原因我也不太清楚,可能与流有关,在此做个笔记,
希望能帮助到与我遇到同样问题的人儿。
2.如果出现org.apache.commons.net.io.CopyStreamException: IOException caught while copying
或者Caused by: java.net.SocketException: Connection reset by peer: socket write error 的异常,
你需要检测:(1)连接是否及时关闭,先logout,再disconnection。(2)磁盘是否已满,这个也是经常发生的。
注意:附件中为自己实现的ftp上传下载工具,包含自己写的超时监听功能(自带的导致阻塞问题)
</pre><pre name="code" class="java">import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;
import com.gg.service.vo.FtpTransferFileVo;
/**
*
* FTP相关操作工具类
*
* @author zzj
* @date May 30, 2016 10:54:20 AM
*/
public class FtpUtil {
/**
* 日志记录器
*/
private static Logger logger = Logger.getLogger(FtpUtil.class);
/**
* ftp服务器ip地址
*/
private String ftpHost = "192.168.128.1";
/**
* ftp服务器登录名
*/
private String ftpUserName = "pds";
/**
* ftp服务器登录密码
*/
private String ftpPassword = "<span style="font-family: Arial, Helvetica, sans-serif;">pds</span>";
/**
* ftp服务器所在的根目录
*/
private String ftpRootDir="";
/**
* ftp服务器端口号
*/
private int ftpPort = 21;
/**
* 文件最大存放天数,0表示永久存储,其他表示到达存放指定存放天数后进行删除文件
*/
private int maxStoreDays=0;
/**
* 流缓冲区存放的大小
*/
private final static int UPLOAD_BUFFER_SIZE=1024*1024;
/**
* 扫描总个数(总监控时间=SCAN_NUMBER*ONE_SECOND*SCAN_SECOND)
* 目前:5*60*(10S)=3000S=50Min
*/
public static final int SCAN_NUMBER = 5*60;
/**
* 一秒
*/
public static final int ONE_SECOND = 1000;
/**
* 每次休眠时间
*/
public static final int SCAN_SECOND = 10;
public static void main(String[] args) {
FtpUtil ftpUtil = new FtpUtil();
String absPath ="/home/test/aa2/aad/";
String fileString="D:/software/MyEclipse_10.7.zip";
//String url ="http://pc2-dx1.newasp.net/soft/good/eclipse-standard-kepler-SR1-win32.zip";
String urlString="http://192.168.92.7/cloud/v1/versionDFS/DCC/AL817_ROW_T_SECBOOT/hq6753_65u_b1o_l1/S1La40_USR_S275_1604251950_MP3V2_16G_ROW/S1La40_USR_S275_1604251950_MP3V2_16G_ROW_DCC/S1La40_USR_S275_1604251950_MP3V2_16G_ROW.rar";
File tempFile = new File(urlString);
String fileName = tempFile.getName();
InputStream io =HttpClientUtil.getUrlInputStream(urlString);
try {
//io = new FileInputStream(new File(fileString));
ftpUtil.uploadHttpFile(io,absPath,fileName);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 将指定远程url的网络文件上传到ftp目录
* @param remoteStorDir ftp的绝对路径(目录+文件名),这个路径必须以/结尾
* @param url 文件所在的http的绝对路径
* @author zzj
* @throws Exception
*/
public boolean uploadHttpFile(InputStream in,String remoteStorDir,String fileName) throws Exception {
logger.info("start uploading file to ftp...");
boolean result = false;
FTPClient ftpClient = null;
String absFilePath=null;
try {
// 创建并登陆ftp服务器
ftpClient = this.getFTPClient(ftpHost, ftpPassword, ftpUserName, ftpPort);
/*ftpClient.setDataTimeout(1000*1000);
ftpClient.setConnectTimeout(connectTimeout)*/
// 设置ftp上传进度监听器
//ftpClient.setCopyStreamListener(createListener());
// 设置PassiveMode被动模式-向服务发送传输请求
ftpClient.enterLocalPassiveMode();
// 设置以二进制流的方式传输
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//添加超时监控线程
new DemaonThread(ftpClient,fileName).start();
// 处理目录的操作
createDirs(ftpClient, remoteStorDir);
logger.info("being upload,please waiting...");
absFilePath = remoteStorDir+fileName;
// 最后,将io流上传到指定的目录
result = ftpClient.storeFile(absFilePath, in);
in.close();
logger.info("the upload result is:"+result+" and file path:" +absFilePath);
} catch (Exception e) {
logger.error("upload file failed,", e);
//删除有可能产生的临时文件
if (absFilePath!=null) {
ftpClient.deleteFile(absFilePath);
}
throw new Exception(e);
} finally {
try {
/*if (!result && absFilePath!=null) {
ftpClient.deleteFile(absFilePath);
System.out.println("删除成功!");
}*/
ftpClient.logout();
if (ftpClient.isConnected()) {
ftpClient.disconnect();
ftpClient=null;
}
} catch (IOException e) {
logger.error("最后操作ftp期间发生异常,",e);
}
}
return result;
}
/**
* 获取FTPClient对象
*
* @param ftpHost FTP主机服务器
* @param ftpPassword FTP 登录密码
* @param ftpUserName FTP登录用户名
* @param ftpPort FTP端口 默认为21
* @return
*/
public FTPClient getFTPClient(String ftpHost, String ftpPassword, String ftpUserName, int ftpPort) {
FTPClient ftpClient = null;
try {
// 连接FTP服务器
ftpClient = new FTPClient();
logger.info("start connect ftp server.");
ftpClient.connect(ftpHost);
//登录到ftp服务器
ftpClient.login(ftpUserName, ftpPassword);
ftpClient.setBufferSize(UPLOAD_BUFFER_SIZE);
//超时时间
int defaultTimeoutSecond=30*60 * 1000;
ftpClient.setDefaultTimeout(defaultTimeoutSecond);
ftpClient.setConnectTimeout(defaultTimeoutSecond );
ftpClient.setDataTimeout(defaultTimeoutSecond);
logger.info("connect and login ftp server success.");
// 设置每次上传的大小
/*ftpClient.setBufferSize(UPLOAD_BUFFER_SIZE);*/
if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
logger.info("未连接到FTP,用户名或密码错误。");
ftpClient.logout();
ftpClient.disconnect();
ftpClient=null;
} else {
System.out.println("FTP connect success!");
}
} catch (SocketException e) {
logger.error("FTP的IP地址可能错误,请正确配置。", e);
} catch (IOException e) {
logger.error("FTP的端口错误,请正确配置。", e);
}
return ftpClient;
}
/**
* 监控ftpclient超时守护线程
* @author zzj
* @date Jun 3, 2016 6:19:18 PM
*/
private class DemaonThread extends Thread {
private FTPClient ftpClient;
private String fileName;
int num = 0;
Long start = System.currentTimeMillis()/1000;
/**
* @param ftpClient2
* @param fileName
*/
public DemaonThread(FTPClient ftpClient2, String fileName) {
this.ftpClient = ftpClient2;
this.fileName=fileName;
}
@Override
public void run() {
while (num < SCAN_NUMBER && ftpClient.isConnected()) {
try {
System.out.println(fileName+",monitor ftpclient start..."+(System.currentTimeMillis()/1000-start)+" S." );
Thread.sleep(ONE_SECOND*SCAN_SECOND);
num++;
System.out.println(fileName+",monitor ftpclient timeout..." );
} catch (Exception e) {
e.printStackTrace();
}
}
try {
System.out.println(fileName+",monitor ftpclient timeout finish...");
ftpClient.logout();
if (ftpClient.isConnected()) {
ftpClient.disconnect();
ftpClient=null;
}
} catch (Exception e) {
System.out.println(fileName+",**********monitor happend error,"+e.getMessage());
}
}
}
/**
* 上传进度监听器(可能导致阻塞)
* @return 监听器对象
* @author zzj
*/
/*public CopyStreamListener createListener() {
return new CopyStreamListener() {
private long start = System.currentTimeMillis()/1000;
@Override
public void bytesTransferred(CopyStreamEvent event) {
System.out.println("transfeerred");
bytesTransferred(event.getTotalBytesTransferred(), event.getBytesTransferred(), event.getStreamSize());
}
@Override
public void bytesTransferred(long totalBytesTransferred, int bytesTransferred, long streamSize) {
System.out.println("Spended time: "+(System.currentTimeMillis()/1000-start)+" seconds.");
System.out.println("transfered total bytes:" + FileUtil.FormetFileSize(totalBytesTransferred) + ",per transfeerred types:" + bytesTransferred+",stream size="+streamSize);
}
};
}*/
/**
* 创建指定的目录
* @param ftpClient ftp客户端
* @param remoteUpLoadPath ftp服务器目录
* @throws IOException
* @author zzj
*/
public static void createDirs(FTPClient ftpClient, String remoteUpLoadPath) throws IOException {
//根据路径逐层判断目录是否存在,如果不存在则创建
//1.首先进入ftp的根目录
ftpClient.changeWorkingDirectory("/");
String[] dirs = remoteUpLoadPath.split("/");
for (String dir : dirs) {
//2.创建并进入不存在的目录
if (!ftpClient.changeWorkingDirectory(dir)) {
int num = ftpClient.mkd(dir);
System.out.println(num);
ftpClient.changeWorkingDirectory(dir);
System.out.println("进入目录成功:"+dir);
}
}
}
/**
* 从ftp下载文件
* @param sourceFtpFileDir 文件所在ftp目录
* @param version 所要取得文件的版本号
* @return 下载后的目录名字
* @throws Exception 异常
* @author zzj
*/
public FTPFile[] listFtpFiles(FTPClient ftpClient ,String sourceFtpFileDir) throws Exception{
logger.info("start downloading file from ftp...");
FTPFile[] ftpFiles = null;
try {
// 设置ftp上传进度监听器
//ftpClient.setCopyStreamListener(createListener("downloading... "));
// 设置PassiveMode被动模式-向服务发送传输请求
ftpClient.enterLocalPassiveMode();
// 设置以二进制流的方式传输
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
//添加超时监控线程
new DemaonThread(ftpClient,"downloading... ").start();
//得到指定目录下所有的文件,指定需要获取的文件的后缀名
ftpFiles =ftpClient.listFiles(sourceFtpFileDir, new FTPFileSuffixFilter(".rar,.zip"));
logger.info("list file size is:"+ftpFiles==null?0:ftpFiles.length);
} catch (Exception e) {
logger.error("download file failed,", e);
throw new Exception(e);
} finally {
}
return ftpFiles;
}
/**
* 需要下载的文件的文件流
* @param ftpClient ftp客户端对象
* @param ftpFiles 获取到的所有文件
* @param version 版本还
* @return 文件流
* @author zzj
* @throws IOException
*/
public Map<String, Object> downloadFtpFile(String fileDir,String version,FtpTransferFileVo transferFileVo) throws Exception{
Map<String, Object> map = new HashMap<String, Object>();
InputStream inputStream = null;
FTPClient ftpClient =null;
try {
ftpClient = this.getFTPClient(this.getFtpHost(), this.getFtpPassword(), this.getFtpUserName(),this.getFtpPort());;
FTPFile[] ftpFiles=this.listFtpFiles(ftpClient, this.getFtpRootDir());
int num=0;
String fileName = null;
for(FTPFile file : ftpFiles){
String tn = file.getName();
tn = tn.substring(0,tn.lastIndexOf("."));
if (file.isFile()&& tn.equals(version)) {
fileName = file.getName();
num++;
}
}
if (num==0) {
throw new Exception("没有找到对应版本【"+version+"】的文件号.");
}else if(num>1){
throw new Exception("对应版本"+version+"的文件大于1个,个数为:"+num+",请人工处理");
}
//设置文件路径
transferFileVo.setFileName(fileName);
transferFileVo.setHttpFileUrl(fileDir+fileName);
boolean flag =ftpClient.changeWorkingDirectory(fileDir);
if (!flag) {
throw new Exception("指定的目录不存在或ftp无法打开,路径为:"+fileDir);
}
System.out.println("进入目录:"+fileDir+"结果:"+flag );
//执行下载文件
inputStream = ftpClient.retrieveFileStream(fileDir+fileName);
map.put("error","false");
} catch (Exception e) {
logger.error("发生异常,",e);
map.put("error", e.getMessage());
}finally{
map.put("stream", inputStream);
map.put("ftpClient", ftpClient);
}
return map;
}
/**
* 所有工厂对应的ftp目录列表
*/
public static Map<String,String> factoryMap = new HashMap<String, String>();
static{
factoryMap.put("ss", "ss");
}
/**
* 根据工厂名得到对应的目录
* @param factory 工厂名
* @return ftp子目录
* @author zzj
*/
public static String getFactoryDir(String factory){
String dirName=null;
for (Map.Entry<String,String> entry : factoryMap.entrySet()) {
String cname=entry.getKey();
if (factory.contains(cname)) {
dirName=entry.getValue();
break;
}
}
return dirName;
}
public String getFtpHost() {
return ftpHost;
}
public void setFtpHost(String ftpHost) {
this.ftpHost = ftpHost;
}
public String getFtpUserName() {
return ftpUserName;
}
public void setFtpUserName(String ftpUserName) {
this.ftpUserName = ftpUserName;
}
public String getFtpPassword() {
return ftpPassword;
}
public void setFtpPassword(String ftpPassword) {
this.ftpPassword = ftpPassword;
}
public int getFtpPort() {
return ftpPort;
}
public void setFtpPort(int ftpPort) {
this.ftpPort = ftpPort;
}
public String getFtpRootDir() {
return ftpRootDir;
}
public void setFtpRootDir(String ftpRootDir) {
this.ftpRootDir = ftpRootDir;
}
public int getMaxStoreDays() {
return maxStoreDays;
}
public void setMaxStoreDays(int maxStoreDays) {
this.maxStoreDays = maxStoreDays;
}
}
package com.gg.service.util;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPFileFilter;
/**
* 获取文件时过滤文件后缀的过滤器
* @author zzj
* @date Jun 7, 2016 3:37:36 PM
*/
public class FTPFileSuffixFilter implements FTPFileFilter{
/**
* 传过来的后缀信息(多个时用英文逗号隔开)
*/
private String fileSuffix;
/**
* 初始化参数
* @param subffix 后缀
*/
public FTPFileSuffixFilter(String subffix){
this.fileSuffix=subffix;
}
/* (non-Javadoc)
* @see org.apache.commons.net.ftp.FTPFileFilter#accept(org.apache.commons.net.ftp.FTPFile)
*/
@Override
public boolean accept(FTPFile file) {
String filename = file.getName();
String[] strings = fileSuffix.split(",");
boolean flag = false;
for(String suf:strings){
if (filename.lastIndexOf(suf) != -1) {
flag = true;
}
}
return flag;
}
}
commons-net只能实现ftp、tftp、ftps等各类ftp的上传下载,如要实现sftp的上传下载,请参考JSCH这个开源jar包,下面为其中的一个示例:
http://www.linux178.com/Java/ftp-sftp-progress.html?utm_source=tuicool&utm_medium=referral 进度示例
http://www.cnblogs.com/dongliyang/p/4173583.html 工具类