/* ==================================================================
* Created [2009-4-27 下午11:32:55] by Jon.King
* ==================================================================
* TSS
* ==================================================================
* mailTo:jinpujun@hotmail.com
* Copyright (c) Jon.King, 2009-2012
* ==================================================================
*/
package com.jinhe.tss.portal.engine.releasehtml;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;
import org.dom4j.Element;
import com.jinhe.tss.core.common.progress.FeedbackProgressable;
import com.jinhe.tss.core.common.progress.Progress;
import com.jinhe.tss.core.exception.BusinessException;
import com.jinhe.tss.core.util.FileHelper;
/**
* <p> _FtpClient.java </p>
* FTP客户端上传类。
*/
public class _FtpClient implements FeedbackProgressable{
protected static Logger log = Logger.getLogger(_FtpClient.class);
private FTPClient ftp;
private String remoteRoot; // 远程FTP根目录
private boolean override; // 是否覆盖上传
/**
* 已上传的文件列表
*/
List<String> uploadedFiles = new ArrayList<String>();
/**
* 上传失败的文件列表
*/
Map<String, String> uploadFailedFiles = new HashMap<String, String>();
public _FtpClient(boolean override) {
this.override = override;
}
/**
* 配置文件格式如下:
* <ftpServer url="192.168.0.31" userName="root" password="password">
* <filePath localDir="d:/project/PMS/webapp/html" remoteDir="temp/html"/>
* <filePath localDir="d:/project/PMS/webapp/core" remoteDir="temp/core"/>
* <filePath localDir="d:/project/PMS/webapp/pms/model" remoteDir="temp/model"/>
* <filter-pattern>index,articleList</filter-pattern>
* </ftpServer>
* @param ftpConfigNode
*/
public void ftpUpload(Element ftpConfigNode) {
List<String> filterPatterns = new ArrayList<String>();
Element filterPattern = (Element) ftpConfigNode.selectSingleNode("filter-pattern");
filterPatterns.addAll(Arrays.asList(filterPattern.getTextTrim().split(",")));
String url = ftpConfigNode.attributeValue("url");
String user = ftpConfigNode.attributeValue("userName");
String pwd = ftpConfigNode.attributeValue("password");
try {
// 连接
connect2FtpServer(url, user, pwd);
for (Iterator<?> itIn = ftpConfigNode.elementIterator("filePath"); itIn.hasNext();) {
Element temp = (Element) itIn.next();
String remoteDir = temp.attributeValue("remoteDir");
String localDir = temp.attributeValue("localDir");
uploadFiles(remoteDir, localDir, filterPatterns);
}
// 关闭连接
disconnectFtpServer();
} catch (IOException e) {
throw new BusinessException("上传文件时出错: " + e.getMessage());
} finally {
log.debug(getFeedback());
}
}
/**
* 连接FTP服务器并登陆。
* @param url
* @param user
* @param pwd
* @throws SocketException, IOException
*/
private void connect2FtpServer(String url, String user, String pwd) throws SocketException, IOException {
this.ftp = new FTPClient();
ftp.connect(url);
ftp.login(user, pwd);
int reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftp.disconnect();
}
this.remoteRoot = ftp.printWorkingDirectory();
}
/**
* 退出FTP服务器并断开连接。
* @throws IOException
*/
private void disconnectFtpServer() throws IOException {
ftp.logout();
if (ftp.isConnected()) {
ftp.disconnect();
}
}
/**
* 将整个文件夹上传。
* @param files
* 所有需要上传文件的列表
* @param remoteDir
* FTP上的目标路径
* @param localDir
* 本地上传文件的目录
*/
private void uploadFiles(String remoteDir, String localDir, List<String> filterPatterns) {
List<File> files = FileHelper.listFilesByTypeDeeply(new File(localDir));
for ( File file : files ) {
String filePath = file.getPath();
try {
makeDirectory(remoteDir, getFileRelativePath(file, localDir));
String fileName = file.getName();
FileInputStream input = new FileInputStream(file);
if (override || hasModifiedRecently(file) || checkByPattern(fileName, filterPatterns)) {
ftp.storeFile(fileName, input);
// 如果上传成功,则将该文件记录到uploadedFiles里
uploadedFiles.add(filePath);
log.info( filePath + "上传结束");
}
input.close();
input = null;
} catch (Exception e) {
uploadFailedFiles.put(filePath, "远程目录(" + remoteDir + ")" + e.getMessage());
// log.error("上传本地文件:" + path + "至远程目录 + " + remoteDir + "过程中出错", e.getCause());
}
updateProgressInfo();
}
}
/**
* 判断要上传的文件名是否含有特殊字符串,从而决定该文件是否需要覆盖上传。
*
* @param fileName
* @return true: 需覆盖,false:不覆盖。
*/
private boolean checkByPattern(String fileName, List<String> filterPatterns) {
if(filterPatterns != null && filterPatterns.size() > 0){
for ( String filterPattern : filterPatterns ) {
if (fileName.indexOf(filterPattern) >= 0)
return true;
}
}
return false;
}
/**
* 文件是否在近段时间内进行过修改。
* @param file
*/
private boolean hasModifiedRecently(File file) {
long limitTime = 8*60*60*1000; // 3小时
return System.currentTimeMillis() - file.lastModified() < limitTime;
}
/**
* 在上传文件夹的过程中,如果服务器的目标目录中和上传文件夹的目录有区别 则创建各级目录。
*
* @param remoteDir
* @param relativePath
*/
private void makeDirectory(String remoteDir, String relativePath) {
try {
ftp.changeWorkingDirectory(this.remoteRoot); // 先切换到根目录下
relativePath = remoteDir + "/" + relativePath;
String[] dir = relativePath.split("/");// 目录分级
for (int i = 0; i < dir.length; i++) {
if (!ftp.changeWorkingDirectory(dir[i])) {// 是否有子目录(dir[i])
ftp.makeDirectory(dir[i]);
}
ftp.changeWorkingDirectory(dir[i]);
}
//log.info("Directory is " + ftp.printWorkingDirectory());
//ftp.changeWorkingDirectory(remoteDir + "/" + relativePath);
} catch (Exception e) {
log.error("在ftp服务器上创建相应目录时候出错", e);
}
}
/**
* 获取上传文件相对于上传目录的文件相对路径(即 文件的绝对路径 - 上传目录路径)。
*
* 例如: 需要上传的文件夹 d:/pms/html,需要上传的文件为:d:/pms/html/2008/12/31/***.htm
* 则返回 2008/12/31/
*
* @param filePath
* @param localDir
* @return
*/
private String getFileRelativePath(File file, String localDir) {
String relativePath = "";
File ld = new File(localDir);
File pfile = file;
while(!(pfile = pfile.getParentFile()).equals(ld) && pfile.getPath().length() > localDir.length()){
if(relativePath.length() > 0){
relativePath = pfile.getName() + "/" + relativePath;
} else {
relativePath = pfile.getName();
}
}
return relativePath;
}
public String getFeedback() {
// 处理上传成功和不成功的文件列表信息
StringBuffer feedback = new StringBuffer("远程返回完成后反馈信息:\n");
feedback.append("上传成功的文件有:\n");
for(int i = 0; i < uploadedFiles.size(); i++){
feedback.append(uploadedFiles.get(i)).append("\n");
}
feedback.append("上传失败的文件有:\n");
for( Entry<String, String> entry : uploadFailedFiles.entrySet() ){
feedback.append( entry.getKey() + ",原因:" + entry.getValue()).append("\n");
}
return feedback.toString();
}
private Progress progress = new Progress(-1);
public void execute(Map<String, Object> params, Progress progress) {
this.progress = progress;
Element ftpConfigNode = (Element) params.get("ftpConfig");
if(ftpConfigNode == null){
throw new BusinessException("ftp服务器配置不能为空");
}
ftpUpload(ftpConfigNode);
}
private int count = 0;
/**
* 更新进度信息
*/
private void updateProgressInfo(){
if(++count % 20 == 0){
progress.add(20); //每20个更新一次进度信息
}
}
}