package com.coship.common.utils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
/**
* FTP工具类,使用Apache的FTP组件实现
*
*/
public class FtpUtil {
private static final Log log = Log.getLog(FtpUtil.class);
public static final int BINARY_FILE_TYPE = FTP.BINARY_FILE_TYPE;
public static final int ASCII_FILE_TYPE = FTP.ASCII_FILE_TYPE;
public static final String ISO_8859_1 = "ISO-8859-1";
public static final char FTP_PATH_CHAR = '/';
/**
*
* 方法download的作用:FTP下载
* 修改记录: [20120504_904032_增加存储路径]
* @param ftpUrl 请求信息 格式:ftp://user:password@ip:port/path/fileName
* @param storePath 文件存储路径
* @param maxSize 文件大小,允许ftp下载远程文件的最大值,单位为byte,超过此大小将不允许下载
* @return
* @throws Exception
* @Exception 异常对象
* @version 1.0[修改时小数点后+1]
*/
public static File download(String ftpUrl,String storePath, long maxSize) throws Exception {
FtpConfInfo infoConf = getInfo(ftpUrl);
if (infoConf == null) {
log.error("构建FTP配置信息失败,请检查:" + ftpUrl);
return null;
}
FTPClient client = null;
File file = null;
try {
client=connectServer(infoConf);
if (client == null) {
log.error("构建FTP客户端失败");
return null;
}
log.debug("FTP服务器连接成功");
if (infoConf.getLocation() != null) {
String[] ss = infoConf.getLocation().split("/");
for (String s : ss) {
client.changeWorkingDirectory(s);
}
}
log.debug("FTP切换目录成功download()");
String fileName = downFile(infoConf ,storePath==null?"":storePath, client, infoConf.getFileName(),
maxSize);
if (fileName == null) {
return null;
}
file = new File(fileName);
if (!file.isFile()) {
log.error("下载的文件失败");
return null;
}
log.debug("文件下载成功准备重命名,file = "+fileName);
String suffix = infoConf.getFileName().substring(infoConf.getFileName().lastIndexOf('.'));
File tempFile = new File(storePath+ File.separator +AssetIDUtil.getOnlyNumberForWebapp() + suffix);
file.renameTo(tempFile);//重命名保持唯一性
file = tempFile;
} catch (Exception e) {
log.error("", e);
throw e;
} finally {
// 关闭ftp
closeServer(client);
}
return file;
}
/**
* FTP批量下载文件到本地目录
*
* @param localPath
* 本地目录
* @param ftpUrlList
* FTP文件列表
* @return localFilePathList 本地文件路径列表
*/
public static List<String> downloadFiles(String localPath,
List<String> ftpUrlList, long maxSize) throws Exception {
List<String> localFilePathList = null;
if (ftpUrlList != null && ftpUrlList.size() > 0) {
localFilePathList = new ArrayList<String>();
FtpConfInfo infoConf = getInfo(ftpUrlList.get(0));
if (infoConf == null) {
log.error("构建FTP配置信息失败,请检查:" + ftpUrlList.get(0));
return null;
}
FTPClient client = null;
try {
client = connectServer(infoConf);
if (client == null) {
log.error("构建FTP客户端失败");
return null;
}
for (String ftpUrl : ftpUrlList) {
infoConf = getInfo(ftpUrl);
if (infoConf.getLocation() != null) {
String[] ss = infoConf.getLocation().split("/");
for (String s : ss) {
client.changeWorkingDirectory(s);
}
}
String fileName = downFile(infoConf ,localPath + File.separator
+ infoConf.getLocation(), client, infoConf
.getFileName(), maxSize);
if (fileName != null) {
localFilePathList.add(fileName);
}
}
} catch (Exception e) {
log.error("", e);
throw e;
} finally {
// 关闭ftp
closeServer(client);
}
}
return localFilePathList;
}
/**
* 判断是否有重名海报
*
* @param ftpUrl
* @param newName
* @return
* @throws Exception
*/
public static boolean checkName(String ftpUrl, String newName)
throws Exception {
FtpConfInfo infoConf = getInfo(ftpUrl);
FTPClient ftpclient = null;
if (infoConf.getLocation() != null) {
ftpclient = connectServer(infoConf);
String[] ss = infoConf.getLocation().split("/");
for (String s : ss) {
if (!existDirectory(ftpclient, s)) {
closeServer(ftpclient);
return true;
}
ftpclient.changeWorkingDirectory(s);
}
}
if (ftpclient == null)
throw new Exception("FTP链接不成功,请检查FTP链接参数!");
if(newName==null)
throw new Exception("检查重名方法的参数newName不允许为空!");
else
newName = new String(newName.getBytes(infoConf.getEncoding()),FtpUtil.ISO_8859_1);
FTPFile[] files = listFiles(ftpclient, newName);
closeServer(ftpclient);
if (files.length > 0)
{
return false;
}
else
{
return true;
}
}
public static void clearPath(String ftpUrl, String newName)
throws Exception {
FtpConfInfo infoConf = getInfo(ftpUrl);
FTPClient ftpclient = null;
if (infoConf.getLocation() != null) {
ftpclient = connectServer(infoConf);
String[] ss = infoConf.getLocation().split("/");
for (String s : ss) {
if (!existDirectory(ftpclient, s)) {
closeServer(ftpclient);
return;
}
ftpclient.changeWorkingDirectory(s);
}
}
if (ftpclient == null)
throw new Exception("FTP链接不成功,请检查FTP链接参数!");
if(newName==null)
throw new Exception("删除文件方法的参数newName不允许为空!");
else
newName = new String(newName.getBytes(infoConf.getEncoding()),FtpUtil.ISO_8859_1);
FTPFile[] files = listFiles(ftpclient, null);
for (FTPFile file : files) {
if (file.isFile()) {
if (!file.getName().equals(newName))
ftpclient.deleteFile(file.getName());
}
}
closeServer(ftpclient);
}
/**
* 上载文件到远程FTP路径中
*
* @param ftpUrl
* eg: ftp://admin:[email protected]:2121/poster/
* @param file
* @return [boolean] false=失败;true=成功.
*/
public static boolean upload(String ftpUrl, File file, String newName)
throws Exception {
FtpConfInfo infoConf = getInfo(ftpUrl);
if (infoConf == null) {
log.error("构建FTP配置信息失败,请检查:" + ftpUrl);
return false;
}
FTPClient ftpclient = connectServer(infoConf);
if (ftpclient == null) {
log.error("构建FTP客户端失败");
return false;
}
if(newName==null)
throw new Exception("上传文件方法的参数newName不允许为空!");
else
newName = new String(newName.getBytes(infoConf.getEncoding()),FtpUtil.ISO_8859_1);
if (infoConf.getLocation() != null) {
String[] ss = infoConf.getLocation().split("/");
for (String s : ss) {
if (!existDirectory(ftpclient, s))
ftpclient.mkd(s);
ftpclient.changeWorkingDirectory(s);
}
}
if (!file.exists()) {
log.error("要上传的文件不存在");
return false;
}
boolean storeResult = false;
InputStream is = null;
try {
is = new FileInputStream(file);
storeResult = ftpclient.storeFile(newName, is);
if(!storeResult){
ftpclient.enterLocalPassiveMode();
storeResult = ftpclient.storeFile(newName, is);
}
log.debug("uploadFTP的当前目录" + ftpclient.printWorkingDirectory());
ftpclient.logout();
closeServer(ftpclient);
if (storeResult)
log.info("文件传输到FTP成功");
else
log.info("文件传输到FTP失败");
return storeResult;
} catch (Exception e) {
log.error("", e);
throw e;
} finally {
if (is != null)
is.close();
}
}
/**
* 删除FTP上的文件
*
* @param ftpUrl
* eg:ftp://admin:[email protected]:2121/IsmpPicture/201006/
* 201006154100845_1083136_jpg
* @return 删除海报失败
* @throws Exception
*/
public static boolean delete(String ftpUrl) throws Exception {
boolean deleteResult = false;
FtpConfInfo infoConf = getInfo(ftpUrl);
if (infoConf == null) {
log.error("构建FTP配置信息失败,请检查:" + ftpUrl);
return false;
}
FTPClient ftpclient = connectServer(infoConf);
if (ftpclient == null) {
log.error("构建FTP客户端失败");
return false;
}
if (infoConf.getFileName() != null) {
if (infoConf.getLocation() != null) {
deleteResult = ftpclient.deleteFile(infoConf.getLocation()
+ "/" + infoConf.getFileName());
} else {
deleteResult = ftpclient.deleteFile(infoConf.getFileName());
}
}
ftpclient.logout();
closeServer(ftpclient);
return deleteResult;
}
/**
* 获得FTP对象信息
*
* @param ftpInfo
* @return FtpConfInfo
*/
private static FtpConfInfo getInfo(String ftpInfo) {
if (ftpInfo == null) {
return null;
}
String regEx = "^ftp://([\\w]+:[\\S]*@)?[\\S]+/[^\\/:*?\"<>|]*$";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(ftpInfo);
if (!m.find()) {
return null;
}
String str = ftpInfo.substring("ftp://".length());
String serverInfo = str.substring(0, str.indexOf("/"));
String fileName = str.substring(str.indexOf("/") + 1);
// if path exist
String path = null;
if (fileName.indexOf("/") > -1) {
path = fileName.substring(0, fileName.lastIndexOf("/"));
fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
}
//解析ftp用户名、密码、IP、端口号 特别是密码含有特殊字符的如: "icdspics:ic:ds!@#@125.210.227.11:98"(lidahu)
int k = serverInfo.indexOf(":");
String serverName =serverInfo.substring(0,k) ;
String server = serverInfo.substring(k+1);
int j = server.lastIndexOf("@");
String serverPwd = server.substring(0,j);
String serverIpPort = server.substring(j+1);
int i = serverIpPort.indexOf(":");
String serverIp = null;
String serverPort = null;
if(i==-1){
serverIp = serverIpPort;
}
else{
serverIp = serverIpPort.substring(0,i);
serverPort= serverIpPort.substring(i+1);
}
FtpConfInfo conf = new FtpConfInfo();
conf.setUser(serverName);
conf.setPassword(serverPwd);
conf.setServer(serverIp);
conf.setLocation(path);
conf.setFileName(fileName);
conf.setMaxWorkTime(60*1000l);//默认60秒完成
if (StringUtils.isNotEmpty(serverPort)) {
try {
conf.setPort(Integer.parseInt(serverPort));
} catch (ClassCastException e) {
// 设置默认端口 21
conf.setPort(21);
}
} else {
// 设置默认端口 21
conf.setPort(21);
}
return conf;
}
/**
* Check the path is exist; exist return true, else false.
*
* @param ftpClient
* @param path
* @return
* @throws IOException
*/
private static boolean existDirectory(FTPClient ftpClient, String path)
throws IOException {
boolean flag = false;
FTPFile[] listFiles;
try {
String Localpath = ftpClient.printWorkingDirectory();
log.debug(Localpath);
listFiles = listFiles(ftpClient, null);
} catch (Exception e) {
log.error("检查FTP路径发送错误",e);
return false;
}
if(listFiles == null)return false;
for(FTPFile ffile :listFiles){
boolean isFile = ffile.isFile();
if(!isFile){
if(ffile.getName().equalsIgnoreCase(path)){
flag = true;
break;
}
}
}
return flag;
}
/**
* (私有方法)连接FTP,返回FTPClient连接[返回根目录],使用完连接后,调用closeServer关闭连接
*
* @param conf
* @return FTPClient
* @throws SocketException
* @throws IOException
*/
private static FTPClient connectServer(FtpConfInfo conf)
throws SocketException, IOException {
FTPClient ftpClient = new FTPClient();
ftpClient.setConnectTimeout(5000);//超时设置,建议采用配置
ftpClient.setDataTimeout(10*1000);//设置数据响应超时时间默认10秒
try {
ftpClient.connect(conf.getServer(), conf.getPort());
} catch (SocketException e ) {
log.error("海报FTP服务器连接超时,请检查FTP服务器地址及端口配置是否正确:FTPServer[" + conf.getServer() + "]--Port[" + conf.getPort() + "]",e);
throw e;
} catch (IOException e ) {
log.error("海报FTP服务器连接超时,请检查FTP服务器地址及端口配置是否正确:FTPServer[" + conf.getServer() + "]--Port[" + conf.getPort() + "]",e);
throw e;
}
if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
if (ftpClient.login(conf.getUser(), conf.getPassword())) {
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
/** 解决中文问题的解码 903889 2010-12-30 Start **/
ftpClient.sendCommand("FEAT");
String featRS = ftpClient.getReplyString();
String encoding = "GBK";
if(featRS.indexOf(" UTF8")!=-1)encoding="UTF8";
if(conf.getLocation()!=null)
conf.setLocation(new String(conf.getLocation().getBytes(encoding),FtpUtil.ISO_8859_1));
if(conf.getFileName()!=null)
conf.setFileName(new String(conf.getFileName().getBytes(encoding),FtpUtil.ISO_8859_1));
conf.setEncoding(encoding);//将获取的编码信息带出来
ftpClient.setActivePortRange(9000, 9100);//主动模式下设置客户方端口范围9000-9100
// ftpClient.enterLocalPassiveMode();//将默认的PORT主动模式改成PASV被动模式
/** 解决中文问题的解码 End **/
return ftpClient;
}
closeServer(ftpClient);
log.error("FTP的用户名和密码不对");
log.error(conf.toString());
return null;
}
closeServer(ftpClient);
log.error("FTP的连接失败");
log.error(conf.toString());
return null;
}
/**
* (私有方法)关闭FTP连接
*
* @param ftpClient
* @throws IOException
*/
private static void closeServer(FTPClient ftpClient) throws IOException {
if (ftpClient != null && ftpClient.isConnected()) {
ftpClient.disconnect();
}
}
/**
* 取得ftp文件
* @param ftp
* @param fileName
* @return
* @throws IOException
*/
private static FTPFile[] listFiles(FTPClient ftp, String fileName)
throws IOException
{
FTPFile[] files = null;
if (StringUtils.isNotEmpty(fileName))
{
files = ftp.listFiles(fileName);
if (files.length != 1)
{
// 如果没有不能下载文件,再用被动模式试一次(lidahu)
ftp.enterLocalPassiveMode();
files = ftp.listFiles(fileName);
}
}
else
{
files = ftp.listFiles();
// 以被动模式再试一次
if (files.length == 0 || files == null)
{
ftp.enterLocalPassiveMode();
files = ftp.listFiles();
}
}
return files;
}
/**
* (私有方法) 下载文件
*
* @param ftp
* @param fileName
* @param maxSize
* 为-1不检查文件大小
* @return String
* @throws UnsupportedEncodingException
* @throws IOException
*/
private static String downFile(FtpConfInfo ftpConfInfo ,String path, FTPClient ftp, String fileName,
long maxSize) throws UnsupportedEncodingException, IOException,
Exception {
if (ftp == null || fileName == null || fileName.length() < 1) {
log.error("文件名为空或不存在");
return null;
}
FTPFile[] files = listFiles(ftp,fileName);
if(files.length != 1){
log.error("PATH:" + path + "|FileName:" + fileName + "当前文件不存在或有多个,实际文件个数为:" + files.length);
return null;
}
FTPFile file = files[0];
if(!file.isFile()){
log.error(file.getName() + "不是文件!");
return null;
}
long lRemoteSize = file.getSize();
// if (lRemoteSize > maxSize && maxSize != -1) {
// log.error("要下载的文件太大,超过maxSize=" + maxSize + "chars");
// return null;
// }
long maxTime = lRemoteSize/1024/50;//50K一秒,
ftpConfInfo.setMaxWorkTime(maxTime*1000);
if (path == null || path.equals("")) {
String temp = System.getProperty("user.dir");
path = temp;
}
File f = new File(path);
if (!f.exists()) {
boolean mkResult = f.mkdirs();
if(!mkResult)log.warn("make dir failed");
}
path = path + File.separator + fileName;
try
{
File localFile = new File(path);
OutputStream output = new FileOutputStream(localFile);
ftp.retrieveFile(fileName, output);
output.close();
}
catch (Exception e)
{
log.error("下载文件"+path+"失败", e);
throw new Exception("FTP传输失败,中断连接");
}
finally
{
FtpUtil.closeServer(ftp);
}
/*
RFS rfs = new RFS(ftpConfInfo,ftp,path,fileName);
rfs.setName("FtpRFS Thread");
rfs.start();
int forInt = 1;
long oldfilesize = 0;
while(forInt < 20){
Thread.sleep(200l);
if(rfs.count >= lRemoteSize)break;//传输完毕,退出.兼容服务器的文件增大情况,不保证增大时完整性
if(rfs.count == oldfilesize){ //相当的时候,增加计数器2秒,则开始中断
forInt++;
}
oldfilesize = rfs.count;//每次处理后,赋值供下次比较
}
if(forInt == 20 ){ //存在FTP传输2秒无响应,中断连接
log.error("FTP传输2秒无响应,中断连接");
rfs.myInterrupt();
throw new Exception("FTP传输2秒无响应,中断连接");
}
log.debug(System.currentTimeMillis()+" "+rfs.toString()+ "="+rfs.getState().toString());
if(rfs.getState()!=Thread.State.TERMINATED){//RFS还存活时,中断
Thread.sleep(100l);
rfs.myInterrupt();
}
*/
return path;
}
public static String repairFtpString(String ftpString)
{
if(StringUtils.isNotEmpty(ftpString) && FTP_PATH_CHAR != ftpString.charAt(ftpString.length()-1))
{
return ftpString+FTP_PATH_CHAR;
}
else
{
return ftpString;
}
}
static class RFS extends Thread{
private FTPClient ftp = null;
volatile long count = 0;
private String path = null;
private String fileName = null;
private FtpConfInfo ftpConfInfo =null;
RFS(FtpConfInfo ftpConfInfo,FTPClient ftp,String path,String fileName){
this.ftp = ftp;
this.path = path;
this.ftpConfInfo =ftpConfInfo;
this.fileName = fileName;
}
public void myInterrupt() throws IOException{
Thread.currentThread().interrupt();
FtpUtil.closeServer(ftp);
}
private void copy(FtpConfInfo ftpConfInfo ,InputStream is ,OutputStream os) throws IOException, Exception{
long timeA = System.currentTimeMillis();
// final StringBuffer timeout = new StringBuffer();
// if (ftpConfInfo!=null&&ftpConfInfo.getMaxWorkTime() > 0) {
// final Timer timer = new Timer();
// timer.schedule(new TimerTask() {
// public void run() {
// timeout.append(true);
// timer.cancel();
// }
// }, ftpConfInfo.getMaxWorkTime());
// }
byte[] buffer = new byte[1024];
count = 0;
int n = 0;
while (-1 != (n = is.read(buffer))) {
// if ("true".equals(timeout.toString())) {
// log.error("FTP数据传输时间超时!");
// throw new Exception("FTP数据传输时间超时!");
// }
os.write(buffer, 0, n);
count += n;
}
long copyTime = (System.currentTimeMillis()-timeA);
if(copyTime<1){
copyTime = 1;
}
log.debug("传输速率为(byte/毫秒):" + (count/copyTime)+"传输时间(毫秒)"+copyTime);
}
public void run(){
InputStream stO = null;
OutputStream stD = null;
try {
stO = new BufferedInputStream(ftp.retrieveFileStream(fileName));
stD = new FileOutputStream(path);
log.debug("准备从FTP拷贝文件");
copy(ftpConfInfo,stO,stD);
stD.flush();
log.debug("文件拷贝完成");
ftp.completePendingCommand();
} catch (Exception e) {
log.error("文件传输异常", e);
} finally {
if (stO != null) {
try {
stO.close();
} catch (IOException e) {
log.error("FTP关闭输入流异常", e);
}
}
if (stD != null) {
try {
stD.flush();
stD.close();
} catch (IOException e) {
log.error("FTP关闭输出流异常", e);
}
}
stO = null;
stD = null;
}
}
}
}