import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.log4j.Logger;

public class FTPClientUtil {

	private final static Logger logger = Logger.getLogger(FTPClientUtil.class);

	private FTPClient ftpclient;
	
	private FtpInfo ftpinfo;

	public FTPClientUtil(FtpInfo ftpinfo) {
		
		this.ftpinfo = ftpinfo;
		
		ftpclient = new FTPClient();
		try {
			ftpclient.connect(ftpinfo.getHost(),ftpinfo.getPort());
			if (ftpinfo.isPassiveMode()) {
ftpclient.enterLocalPassiveMode();
			} else {
				ftpclient.enterLocalActiveMode();
			}
			if (ftpinfo.getUsername() != null) {
				if (!ftpclient.login(ftpinfo.getUsername(), ftpinfo.getPassword())) {
					ftpclient.disconnect();
					logger.error("登陆服务器" + ftpinfo + "验证失败,请检查你的ftp账号和密码是否正确 !");
				}
			}
		} catch (IOException e) {
			logger.error("登陆服务器" + ftpinfo + "验证失败,发生IO错误:",e);
		}
	}
	
	/***
	 * 只上传一个本地文件,上传完毕之后记得调用close()方法
	 * 
	 * @param remotePath
	 *            文件的远程路径 以/开头
	 * @param localPath
	 *            文件的本地路径
	 * @return
	 */
	public boolean uploadFile(String remotePath, String localPath) {
		boolean RT = false;
		File localfile = new File(localPath);
		FileInputStream input = null;
		try {
				
			input = new FileInputStream(localfile);
			RT = upload(remotePath, input, localfile.length());
			if (!RT) {
				logger.error("上传文件"+ localfile+ "到"+ ftpinfo.getHost()+ remotePath+ "时发生未知错误!上传失败!");
			} else {
				logger.info("上传文件" + localfile + "到" + ftpinfo.getHost() + "成功!远程路径:" + remotePath);
			}
		} catch (IOException e) {
			logger.error("上传文件" + localPath + "时出错!",e);
		}finally{
			if(input != null){
				try {
					input.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		return RT;
	}
	/**
	 * 
	 * @param path
	 *            文件在ftp上的存储路径
	 * @param input
	 *            ftp输入流
	 * @throws IOException
	 */
	private boolean upload(String pathname, InputStream input, long localsize){
		
		boolean b = false;
		try{
			//登陆后设置文件类型为二进制否则可能导致乱码文件无法打开 
			ftpclient.setFileType(FTP.BINARY_FILE_TYPE);
			if (pathname.indexOf("/") != -1) {
				String path = pathname.substring(0, pathname.lastIndexOf("/"));
				mkdir(path);
			}
			//复制文件
			b = ftpclient.storeFile(new String(pathname.getBytes(), ftpclient
					.getControlEncoding()), input);
			input.close();
			//切换到需要下载的FTP目录
			ftpclient.changeWorkingDirectory("/");
		}catch (Exception e) {
			logger.error("上传时出错:",e);
		}finally{
			try {
				input.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return b;
	}

	/**
	 * 删除一个远程文件
	 * 
	 * @param pathname
	 *            远程文件路径
	 * @return 删除成功返回true,其他如果路径不存在或删除失败,返回false;
	 * @throws IOException
	 */
	public boolean delete(String pathname) throws IOException {
		return ftpclient.deleteFile(new String(pathname.getBytes(), ftpclient
				.getControlEncoding()));
	}

	public boolean changeWorkingDirectory(String pathname) throws IOException {
		return ftpclient.changeWorkingDirectory(new String(pathname.getBytes(),
				ftpclient.getControlEncoding()));
	}

	/***
	 * 创建一级或多级目录结构
	 * 
	 * @param pathname
	 *            /flod1/fload2
	 * @return
	 * @throws IOException
	 */
	private boolean mkdir(String pathname) throws IOException {

		boolean makesuccess = false;
		pathname = pathname.replace("\\", "/").replaceAll("^\\/|\\/$", "");
		if ("".equals(pathname)) {
			return true;
		}
		String[] path = pathname.split("/");
		String predir = "";
		for (int i = 0; i < path.length; i++) {
			String thisLevel = new String(path[i].getBytes(), ftpclient
					.getControlEncoding());
                  //上传时使用控制端编码,即FTPClient.getControlEncoding()函数来转换文件名编码,
			      //这样上传之后在linux上可以识别中文,而且在IE,Firefox上都可以识别,编码为GB2312。 			
			ftpclient.makeDirectory(thisLevel);
			predir += "/" + thisLevel;
			makesuccess = ftpclient.changeWorkingDirectory(predir);
		}
		return makesuccess;
	}

	/***
	 * 退出登录并关闭ftp连接
	 */
	public void close() {
		try {
			if (ftpclient != null) {
				ftpclient.logout();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			if (ftpclient != null) {
				ftpclient.disconnect();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
	
	public static void close(Closeable c) {
		if (c != null) {
			try {
				c.close();
			} catch (IOException e) {
				// ignored
				e.printStackTrace();
			}
		}
	}
	
	/***
	 * 判断远程文件是否存在
	 * @param remotePath
	 * @return
	 */
	public boolean exist(String remotePath){
		
		boolean fileExist = false;
		try {
			FTPFile[] files = ftpclient.listFiles(remotePath);
			fileExist = files.length == 1;

		} catch (IOException e) {
			e.printStackTrace();
		}
		return fileExist;
		
	}
	
	public static URLConnection getURLConnection(String urlPath, boolean write) throws IOException {
		URL url = new URL(urlPath);
		URLConnection urlConnection = url.openConnection();
		urlConnection.setDoOutput(write);
		urlConnection.setConnectTimeout(60 * 1000); // 链接超时为1分钟
		urlConnection.setReadTimeout(60 * 1000); // 读数据超时为1分钟
		return urlConnection;
	}
	 /**
	 * 将输入流转村为文件,可获取http,ftp等数据流
	 * 
	 * @param urlPath
	 * @param file
	 * @throws IOException
	 */
	public static String downStreamToFile(String urlPath) throws IOException {
		String str = "";
		ByteArrayOutputStream outStream = null;
		InputStream inputStream = null;
		try {
			URLConnection urlConnection = getURLConnection(urlPath, true);
			inputStream = urlConnection.getInputStream();
			outStream = new ByteArrayOutputStream();
			byte[] buffer = new byte[10240];
			int n_bytes;
			while ((n_bytes = inputStream.read(buffer)) != -1) {
				outStream.write(buffer, 0, n_bytes);
				outStream.flush();
			}
			str = outStream.toString();
		} finally {
			close(inputStream);
			close(outStream);
		}
		return str;
	}
	
	/**
	 * 文件下载
	 * @param map
	 * @param ftpPath
	 * @return
	 */
	public static String downloadFile(Map<String, String> map,String ftpPath) {
		String str = "";
		FTPClient ftp = null;
		boolean connectT = false; //连接FTP标示
		try {
			for (int i=0;i<3;i++)
			{
				try
				{
					if (connectT == false)
					{
						logger.info("开始连接FTP:第"+(i+1)+"次");
						ftp = new FTPClient();
						ftp.setDefaultTimeout(5000);
						ftp.connect(map.get("ip"), Integer.valueOf(map.get("port")));
						ftp.login(map.get("username"), map.get("password"));
						str = downStreamToFile(ftpPath);
						connectT = true;
						break;
					}
				}
				catch(Exception e)
				{
					logger.info("连接FTP失败,尝试重新连接......");
					connectT = false;
				}
			}
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		} 
        finally {

			if (ftp.isConnected()) {
				try {
					ftp.disconnect();
				} catch (IOException ioe) {
				}
			}
		}
		return str;
	}
	
}
public class FtpInfo {

	@Override
	public String toString() {
		return "[ftp://" +username + ":"+password + "@" + host + ":" + port +"]";
	}
	private String host;
	private int port;
	private String username;
	private String password;
	private String ftppath;
	private boolean passiveMode;
	
	public boolean isPassiveMode() {
		return passiveMode;
	}
	public void setPassiveMode(boolean passiveMode) {
		this.passiveMode = passiveMode;
	}
	public String getHost() {
		return host;
	}
	public void setHost(String host) {
		this.host = host;
	}
	public int getPort() {
		return port;
	}
	public void setPort(int port) {
		this.port = port;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getFtppath() {
		return ftppath;
	}
	public void setFtppath(String ftppath) {
		this.ftppath = ftppath;
	}
	
}
connect(String hostname, int port) 
与制定地址和端口的 FTP 站点建立 Socket 连接 

 login(String username, String password) 
 使用制定用户名和密码登入 FTP 站点 

 logout() 
 通过发送 QUIT 命令,登出 FTP 站点 

 disconnect() 
 关闭和 FTP 站点的连接,并重置所有连接参数为初始值 

 sendNoOp() 
 发送 NOOP 命令至 FTP 站点,与 noop() 类似。防止连接超时,也可以根据返回值检查连接的状态。 

 setBufferSize(int bufSize) 
 设置内部缓冲区大小 

 setFileType(int fileType) 
 设置文件传输的方式 

 enterLocalPassiveMode() 
 在建立数据连接之前发送 PASV 命令至 FTP 站点,将数据连接模式设置为被动模式。 

 enterLocalActiveMode() 
 在建立数据连接之前将数据连接模式设置为主动模式。 

 changeWorkingDirectory(String pathname) 
 改变当前 FTP 会话的工作目录 

 listFiles() 
 发送 LIST 命令至 FTP 站点,使用系统默认的机制列出当前工作目录的文件信息 

 makeDirectory(String pathname) 
 在当前工作目录下新建子目录 

 retrieveFile(String remote, OutputStream local) 
  取得 FTP 站点上的指定文件并写入指定的字节流中 

 storeFile(String remote, InputStream local) 
  将指定的输入流写入 FTP 站点上的一个指定文件