天天看點

多線程下載下傳圖檔并壓縮

最近在做一個需求:從其他系統的ftp目錄下載下傳存儲圖檔url的檔案,然後讀取檔案中的url位址,根據位址下載下傳圖檔後按天壓縮成一個包,平均一個位址檔案中包含4000個位址左右,也就是說一個檔案掃描後需要下載下傳4000個左右的圖檔,然後壓縮,下面把我的實作方式和優化過程記錄下來,如果大家有什麼更好的方式可以分享。

使用架構:SpringMVC

定時任務實作:繼承org.springframework.scheduling.quartz.QuartzJobBean;

ftp環境搭建就不說了,在其他部落格記錄過,使用虛拟機中的CentOS搭建的FTP服務,建立FTP賬号及對應目錄,事先上傳需要下載下傳的圖檔位址檔案。檔案内容格式“圖檔ID||圖檔位址”。

方法一、最簡單的實作方法就是先下載下傳存儲圖檔url位址的檔案,然後讀取檔案周遊圖檔位址,調下載下傳圖檔的方法将圖檔存儲到本地,最後壓縮下載下傳的圖檔,完成後删除下載下傳的圖檔,隻保留壓縮包。

public class PictureTransferJob extends QuartzJobBean
{
   protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException
    {
	//實際的FTP配置是讀取配置檔案擷取的
	//FTP位址
	String hostName ="192.168.1.112";
	//FTP端口
	int port = 2001;
	/FTP賬号
	String userName = "test1";
	//ftp密碼
	String password = "test1";
	//ftp檔案存儲目錄
	String ftpDowload =  "/";
	//檔案本地存儲路徑
	String path = this.getClass().getResource("/").getPath();
	//圖檔位址檔案存儲目錄
	String addrPath=path.substring(1, path.indexOf("WEB-INF/classes"))+"picAddr";
	//實際下載下傳的圖檔存儲目錄
	String picPath=path.substring(1, path.indexOf("WEB-INF/classes"))+"pic";
	addrPath = addrPath.replace("%20"," ");
	picPath = picPath.replace("%20"," ");
	try
        {
	    //建立存儲圖檔位址的檔案
	    creatFile(addrPath);
	    //建立存儲實際圖檔的檔案
	    creatFile(picPath);
	    String oldAddrPath = addrPath;
	    String oldPicPath = picPath;
	    //建立FTP連接配接
	    FtpUtil2 ftpUtil2 = new FtpUtil2(hostName, port,userName, password, ftpDowload, true);
	    //周遊FTP目錄下的檔案
	    String[] files = ftpUtil2.ListAllFiles();
	    //本地資料庫會有一個表記錄下載下傳過的檔案,這裡會查詢資料庫和ftp列出的檔案名比較,如果已經下載下傳過的檔案就不會下載下傳,避免重複下載下傳。
	    //下面省略比較的過程,循環files數組,在本地建立檔案
	    for(int i=0;i<files.length;i++){
		creatFile(addrPath+File.separator+fileName);
		//ftpDowload是ftp伺服器存儲檔案的位址,addrPath是本地存儲檔案的位址
		//這裡一個傳回狀态判斷檔案是否下載下傳成功
		boolean downloadInvestorFlag =  ftpUtil2.downloadFile(ftpDowload, addrPath);
		//檔案下載下傳成功後調讀取檔案的方法,将需要下載下傳的圖檔位址存入容器
		boolean entityState = setPictureDetail(addrPath,picPath,fileNameDate);
	    }
	}			
	catch (Exception e)
        {
            e.printStackTrace();
            //調記錄錯誤日志的業務類用于發送下載下傳檔案出錯的短信
        }
}
			
//這裡開始讀圖檔位址
private boolean setPictureDetail(String addrPath,String picPath,String synDate)
{
        System.out.println("----------進入setPictureDetail方法-----------");
        BufferedReader br = null;
        try
        {
            br=new BufferedReader(new InputStreamReader(new FileInputStream(addrPath),"UTF-8"));
            String row;
            int count=0;
	    //map中存儲每行讀取到的圖檔名稱和URL位址
            Map<String, String> addrMap=new HashMap<String, String>();
            while ((row=br.readLine())!=null)
            {
                try
                {
                    count++;
                    if (count==1)
                    {
                        continue;
                    }
                    String[] column = row.split("\\|\\|", -1);
                    addrMap.put(column[0].trim(), column[1].trim());
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
            System.out.println(new Date());
            //這裡調用壓縮方法,壓縮方法中會調用執行下載下傳圖檔的方法
            zipPic(picPath,synDate,addrMap);
            System.out.println(new Date());
            System.out.println("----------完成--------------");
            return true;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            //調用記錄錯誤日志的業務類
            return false;
        }finally {
            try {
                if (null != br)
                    br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 根據url位址下載下傳圖檔
     * @throws IOException 
     */
    private boolean downPic(String picPath,List<Entry<String, String>> addrList,List<File> picList)throws IOException{
        InputStream is=null;
        FileOutputStream fos=null;
        URL url=null;
        String fileName=null;
        String picAddr=null;
        File pic=null;
        try
        {
            for(Map.Entry<String, String> addrEntry:addrList)
            {
                fileName=addrEntry.getKey();
                picAddr=addrEntry.getValue();
                //建立Url對象
                url=new URL(picAddr);
                is=url.openStream();
                //URLConnection擷取到的流通過InputStream直接寫入位元組數組會缺失資料,導緻下載下傳的圖檔不完整,使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解決
                byte[] bytes=IOUtils.toByteArray(is);//new byte[is.available()];擷取的位元組
                //流中資料讀入位元組數組,讀入後,流中資料清空
                pic=new File(picPath+fileName+".jpg");
                fos=new FileOutputStream(pic);
                fos.write(bytes);
                //将下載下傳的圖檔存入List,待圖檔全部下載下傳完成後傳入zip方法進行壓縮
                picList.add(pic);
                fos.flush();
                fos.close();
                is.close();
            }
            return true;
        }    
        catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }
        finally{
        	  if (null!=fos)
            {
                fos.close();
            }
            if (null!=is)
            {
                is.close();
            }
        }
    }
    //這裡是壓縮檔案的僞代碼
    private void zipPic(picPath,synDate,addrMap);{
    //傳入需要壓縮的檔案清單和壓縮檔案名
    ZipUtil.zipByStream(picList,new File(picPath+synDate+".zip"));
    }
    /**
     * 建立檔案
     * @param path
     */
    private void creatFile(String path)
    {
        File file = new File(path);
        if(!file.exists())
        {
            file.mkdirs();
        }
    }
}
           

方法二、多線程下載下傳、直接壓縮流

方法一雖然實作了基本功能,但是由于需要下載下傳的圖檔太多,以及壓縮本地圖檔檔案和删除圖檔也比較耗時,是以可以優化速度的地方有兩個。一個就是提高下載下傳圖檔的效率,一個就是提高壓縮的效率。

提高下載下傳效率的方法可以使用多線程下載下傳,提高壓縮效率的方法是可以不将圖檔儲存到本地而直接壓縮檔案流。

多線程實作方式:首先我們儲存了需要下載下傳的檔案的位址清單,我們要使用多線程下載下傳就要保證不同線程下載下傳的圖檔不會重複,是以需要一個标志來區分,這時就可以使用一個索引計數器,按每個線程下載下傳一定量圖檔分割,從0開始,每隔比如400個圖檔就用一個線程下載下傳,這樣就可以确定需要的線程個數,并且每個線程下載下傳的圖檔不會重複。

壓縮檔案實作方式:因為生成壓縮檔案的本質也是讀取需要壓縮的檔案流,然後生成壓縮包,是以我們可以不建立下載下傳的圖檔檔案,而直接使用容器存儲所有線程下載下傳的圖檔流資料,然後将流資料傳給壓縮工具類直接壓縮,這樣就省略了讀取圖檔檔案建立流,然後生成壓縮包,再删除本地圖檔檔案的繁瑣過程。

下面列出改造的主要實作:

/**
 * 将下載下傳的圖檔按天壓縮
 * @throws IOException 
 */
private boolean zipPic(String picPath,String synDate,Map<String, String> addrMap) throws IOException{
   //這裡由于是多線程存儲圖檔流,是以需要使用線程安全的map,是以使用ConcurrentHashMap
    Map<String,InputStream> pictureList=new ConcurrentHashMap<String,InputStream>();
    //這裡定義每個線程下載下傳的圖檔個數
    int count=400;
    //存儲需要下載下傳的圖檔位址
    List<Entry<String, String>> addrList=new ArrayList<Entry<String, String>>(addrMap.entrySet());
    //線程數,加一是因為要建立一個線程下載下傳最後不足400個的圖檔
    int nThreads=(addrList.size()/count)+1;
    //CountDownLatch countDownLatch = new CountDownLatch(nThreads);
    try
    {
        boolean downPic=false;
        //執行多線程下載下傳圖檔
        downPic=downPic(picPath,addrList,picList,pictureList,nThreads,count);
        if (downPic)
        {
            ZipUtil.zipByArray(picList,new File(picPath+synDate+".zip"));
        }
        return true;
    }
    catch (Exception e)
    {
        e.printStackTrace();
        return false;
    }
}
           

下面是建立線程池

/**
 * 根據url位址下載下傳圖檔
 * @throws InterruptedException 
 */
private boolean downPic(String picPath,List<Entry<String, String>> addrList,Map<String, byte[]> picList,Map<String, InputStream> pictureList,int nThreads,int count)throws IOException, InterruptedException{
    ExecutorService threadPool=Executors.newFixedThreadPool(nThreads);
    // 建立兩個個計數器
        CountDownLatch begin=new CountDownLatch(0);
        CountDownLatch end=new CountDownLatch(nThreads);
    // 循環建立線程
    for (int i = 0; i < nThreads; i++) {
        List<Entry<String, String>>subAddrList=null;
        // 計算每個線程執行的資料
        if ((i + 1) == nThreads) {
            int startIndex = (i * count);
            int endIndex = addrList.size();
            subAddrList = addrList.subList(startIndex, endIndex);
        } else {
            int startIndex = (i * count);
            int endIndex = (i + 1) * count;
            subAddrList = addrList.subList(startIndex, endIndex);
        }
        // 線程類
        PicDownload mythead = new PicDownload(picPath,subAddrList,picList,pictureList);
        // 這裡執行線程的方式是調用線程池裡的threadPool.execute(mythead)方法。
        try
        {
            threadPool.execute(mythead);
        }
        catch (Exception e)
        {
            //記錄錯誤日志
            return false;
        }
    }
    begin.countDown();
    end.await();
    // 執行完關閉線程池
    threadPool.shutdown();
    //這裡一定要循環直到線程池中所有線程都結束才能往下走,測試時由于沒有這一步導緻子線程下載下傳圖檔還沒完成,而主線程已經往下走了,導緻壓縮包内沒有圖檔
    //也可以使用CountDownLatch實作
    /*while (true)
    {
        if (threadPool.isTerminated())
        {
            System.out.println("所有子線程已結束!");
            break;
        }
    }*/
    return true;
}
           

下面是線程實作

class PicDownload implements Runnable{
    //下載下傳圖檔的位址清單
    List<Entry<String, String>> addrList;
    //裝載下載下傳成功的圖檔清單
    Map<String, byte[]> picList;
    Map<String, InputStream> pictureList;
    //圖檔本地存儲路徑
    String picPath;
    
    CountDownLatch begin,end;
    public PicDownload(String picPath,List<Entry<String, String>> addrList,Map<String, InputStream> picList,CountDownLatch begin,CountDownLatch end){
        this.addrList=addrList;
        this.picList=picList;
        this.picPath=picPath;
        this.begin=begin;
        this.end=end;
    }
    @Override
    public void run()
    {
        try
        {
            System.out.println(Thread.currentThread().getName()+"------"+Thread.currentThread().getId());
            downPicture(addrList);
            //System.out.println(countDownLatch.getCount());
            begin.await();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            end.countDown();
            //countDownLatch.countDown();
        }
    }
    public boolean downPicture(List<Entry<String, String>> addrList) throws Exception{
        InputStream is=null;
        FileOutputStream fos=null;
        URL url=null;
        String fileName=null;
        String picAddr=null;
        File pic=null;
        try
        {
            for(Map.Entry<String, String> addrEntry:addrList)
            {
                fileName=addrEntry.getKey();
                picAddr=addrEntry.getValue();
                //建立Url對象
                url=new URL(picAddr);
                is=url.openStream();
                //URLConnection擷取到的流通過InputStream直接寫入位元組數組會缺失資料,導緻下載下傳的圖檔不完整,使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解決
                //byte[] bytes=IOUtils.toByteArray(is);//new byte[is.available()];擷取的位元組
                //流中資料讀入位元組數組,讀入後,流中資料清空
                picList.put(fileName+".jpg", is);
                //這時候由于沒有把流寫入檔案,一定不能關閉流,否則流中的資料就會丢失
                //is.close();
            }
            return true;
        }    
        catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }
        finally{
            //不能關閉流
            /*if (null!=is)
            {
                is.close();
            }*/
        }
    }
}
           

上面使用流來壓縮遇到了另一個問題,在壓縮檔案時會出現java.net.SocketException:Connection reset

分析了一下原因,應該是由于流InputStream和UrlConnection是連接配接狀态的,UrlConnection逾時重置導緻了擷取輸入流失敗。

嘗試設定URLConnection的逾時時間,但是測試時發現圖檔下載下傳收到網速影響較大,這種方式很不穩定,不可取,最後隻有放棄使用流,而改用位元組數組傳給壓縮工具類,然後将位元組數組轉為流壓縮。

/**
*使用容器存儲下載下傳的圖檔位元組數組
*/
public boolean downPicture(List<Entry<String, String>> addrList) throws Exception{
    InputStream is=null;
    FileOutputStream fos=null;
    URL url=null;
    String fileName=null;
    String picAddr=null;
    File pic=null;
    try
    {
        for(Map.Entry<String, String> addrEntry:addrList)
        {
            fileName=addrEntry.getKey();
            picAddr=addrEntry.getValue();
            //建立Url對象
            url=new URL(picAddr);
            //打開連接配接,建立java.net.URLConnection對象,該對象沒有關閉連接配接的方法,可以轉為它的子類HttpURLConnection調用disconnect方法關閉連接配接。
            //java.net.URLConnection和java.net.HttpURLConnection都有設定逾時時間的方法關閉連接配接
            //HttpURLConnection uc=(HttpURLConnection)url.openConnection();
            is=uc.getInputStream();
            //URLConnection擷取到的流通過InputStream直接寫入位元組數組會缺失資料,導緻下載下傳的圖檔不完整,使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解決
            byte[] bytes=IOUtils.toByteArray(is);//new byte[is.available()];擷取的位元組
            //流中資料讀入位元組數組,讀入後,流中資料清空
            //is.read(bytes);
            picList.put(fileName+".jpg",bytes);
            is.close();
        }
        return true;
    }    
    catch (Exception e)
    {
        e.printStackTrace();
        return false;
    }
    finally{
        if (null!=is)
        {
            is.close();
        }
    }
}
           

總結:

實作過程中遇到的問題:

1、使用線程池時對于共享狀态,比如這裡的存儲下載下傳的圖檔位元組資料容器是所有線程共享的,是以需要使用同步的容器,否則會導緻存儲的資料出問題,是以使用了ConcurrentHashMap<String,byte[]>

2、這裡存在一個主線程和子線程的執行順序問題,因為主線程需要等待線程池中所有線程下載下傳圖檔結束後才能往下走去壓縮圖檔,如果主線程不等待子線程結束就向下執行壓縮方法就會導緻壓縮圖檔缺少或者沒有壓縮圖檔。是以可以使用CountDownLatch實作,或者在關閉線程池語句下面使用死循環檢查threadPool.isTerminated()才能繼續執行主線程去壓縮圖檔。

3、由于直接将UrlConnection擷取到的輸入流直接傳給壓縮類進行壓縮,存在連接配接逾時重置的情況,是以改用将下載下傳的流存入位元組數組,再傳給壓縮類壓縮,避免使用流出現意外情況。

4、在使用urlconnection.openStream()擷取輸入流後,轉換為位元組數組下載下傳的圖檔是不完整的。。使用org.apache.commons.io.IOUtils.toByteArray(urlconnection.openstream())可以解決,具體可以閱讀其源碼看實作。

下面是FTP工具類的實作:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPConnectionClosedException;
import org.apache.commons.net.ftp.FTPReply;

public class FtpUtil2 {
	private FTPClient ftpClient = null;

	// ftp伺服器位址
	private String hostName;

	// ftp伺服器預設端口
	public static int defaultport = 21;

	// 登入名
	private String userName;

	// 登入密碼
	private String password;

	// 需要通路的遠端目錄
	private String remoteDir;

	/** */
	/**
	 * @param hostName
	 *            主機位址
	 * @param port
	 *            端口号
	 * @param userName
	 *            使用者名
	 * @param password
	 *            密碼
	 * @param remoteDir
	 *            預設工作目錄
	 * @param is_zhTimeZone
	 *            是否是中文FTP Server端
	 * @return
	 * @return
	 */
	
	/**
	 * 新增方法
	 */
	public FtpUtil2()
	{	
		PropConfig config = PropConfig.loadConfig("system.properties");
		String hostName = config.getConfig("ftpAddress");
		String port = config.getConfig("ftpPort");
		String userName = config.getConfig("ftpUserName");
		String password = config.getConfig("ftpPassword");
		String remoteDir = config.getConfig("remoteFilePath");
		boolean is_zhTimeZone= true;
		this.hostName = hostName;
		this.userName = userName;
		this.password = password;
		this.remoteDir = remoteDir == null ? "" : remoteDir;
		this.ftpClient = new FTPClient();
		if (is_zhTimeZone) {
			this.ftpClient.configure(FtpUtil2.Config());
			this.ftpClient.setControlEncoding("GBK");
		}
		// 登入
		this.login();
		// 切換目錄
		this.changeDir(this.remoteDir);
		this.setFileType(FTPClient.BINARY_FILE_TYPE);
		ftpClient.setDefaultPort(Integer.parseInt(port));		
	}
	public FtpUtil2(String hostName, int port, String userName,
			String password, String remoteDir, boolean is_zhTimeZone) {
		this.hostName = hostName;
		this.userName = userName;
		this.password = password;
		defaultport=port;
		this.remoteDir = remoteDir == null ? "" : remoteDir;
		this.ftpClient = new FTPClient();
		if (is_zhTimeZone) {
			this.ftpClient.configure(FtpUtil2.Config());
			this.ftpClient.setControlEncoding("GBK");

		}
		// 登入
		this.login();
		// 切換目錄
		this.changeDir(this.remoteDir);
		this.setFileType(FTPClient.ASCII_FILE_TYPE);
		ftpClient.setDefaultPort(port);

	}

	/**
	 * 登入FTP伺服器
	 */
	public boolean login() {
		boolean success = false;
		try {
			ftpClient.connect(this.hostName,defaultport);
			ftpClient.login(this.userName, this.password);
			int reply;
			reply = ftpClient.getReplyCode();
			if (!FTPReply.isPositiveCompletion(reply)) {
				ftpClient.disconnect();
				return success;
			}
		} catch (FTPConnectionClosedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		success = true;
		System.out.println("連接配接到ftp伺服器:" + this.hostName + " 成功..開始登入");
		return success;
	}

	private static FTPClientConfig Config() {
		FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
		conf.setRecentDateFormatStr("MM月dd日 HH:mm");
		// conf.setRecentDateFormatStr("(YYYY年)?MM月dd日( HH:mm)?");
		return conf;
	}

	/**
	 * 變更工作目錄
	 * 
	 * @param remoteDir
	 * 
	 */
	public void changeDir(String remoteDir) {
		try {
			this.remoteDir = remoteDir;
			ftpClient.changeWorkingDirectory(remoteDir);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("變更工作目錄為:" + remoteDir);
	}

	/**
	 * 傳回上一級目錄(父目錄)
	 */
	public void toParentDir() {
		try {
			ftpClient.changeToParentDirectory();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 列出目前工作目錄下所有檔案
	 */
	public String[] ListAllFiles() {
		String[] names = this.ListFiles("*");
		return this.sort(names);
	}

	/**
	 * 列出指定工作目錄下的比對檔案
	 * 
	 * @param dir
	 *            exp: /cim/
	 * @param file_regEx
	 *            通配符為*
	 */
	public String[] ListAllFiles(String dir, String file_regEx) {
		String[] names = this.ListFiles(dir + file_regEx);
		return this.sort(names);
	}

	/**
	 * 列出比對檔案
	 * 
	 * @param file_regEx
	 *            比對字元,通配符為*
	 */
	public String[] ListFiles(String file_regEx) {
		try {
			/**//*
				 * FTPFile[] remoteFiles = ftpClient.listFiles(file_regEx);
				 * //System.out.println(remoteFiles.length); String[] name = new
				 * String[remoteFiles.length]; if(remoteFiles != null) { for(int
				 * i=0;i<remoteFiles.length;i++) { if(remoteFiles[i] == null)
				 * name[i] = ""; else
				 * if(remoteFiles[i].getName()==null||remoteFiles
				 * [i].getName().equals
				 * (".")||remoteFiles[i].getName().equals("..")) { name[i] = "";
				 * } else name[i] = remoteFiles[i].getName();
				 * System.out.println(name[i]); } }
				 */
			ftpClient.enterLocalPassiveMode();
			String[] name = ftpClient.listNames(file_regEx);;
			if (name == null)
				return new String[0];

			return this.sort(name);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return new String[0];
	}

	public void Lists(String reg) {
		try {
			String[] a = ftpClient.listNames(reg);
			if (a != null) {
				for (String b : a) {
					System.out.println(b);
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 設定傳輸檔案的類型[文本檔案或者二進制檔案]
	 * 
	 * @param fileType
	 *            --BINARY_FILE_TYPE,ASCII_FILE_TYPE
	 */
	public void setFileType(int fileType) {
		try {
			ftpClient.setFileType(fileType);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 上傳檔案
	 * 
	 * @param localFilePath
	 *            --本地檔案路徑+檔案名
	 * @param newFileName
	 *            --新的檔案名
	 */
	public void uploadFile(String localFilePath, String newFileName) {
		// 上傳檔案
		this.ftpClient.enterLocalPassiveMode();// 被動模式連接配接
		BufferedInputStream buffIn = null;
		try {
			buffIn = new BufferedInputStream(new FileInputStream(localFilePath));
			boolean ifUpload = ftpClient.storeFile(newFileName, buffIn);
			if (!ifUpload) {
				System.out.println("上傳檔案失敗。。。");
			} else {
				System.out.println("上傳檔案成功。。。");
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (buffIn != null)
					buffIn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 上傳檔案2
	 * 
	 * @param file
	 *            --FileInputStream的檔案
	 * @param newFileName
	 *            --新的檔案名
	 */
	public void newUploadFile(FileInputStream file, String newFileName) {
		// 上傳檔案
		this.ftpClient.enterLocalPassiveMode();// 被動模式連接配接
		BufferedInputStream buffIn = null;
		try {
			buffIn = new BufferedInputStream(file);
			boolean ifUpload = ftpClient.storeFile(newFileName, buffIn);
			if (!ifUpload) {
				System.out.println("上傳檔案失敗。。。");
			} else {
				System.out.println("上傳檔案成功。。。");
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (buffIn != null)
					buffIn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	

	/**
	 * 下載下傳檔案(單個)
	 * 
	 * @param remoteFileName
	 *            --伺服器上的檔案名
	 * @param localFileName
	 *            --本地檔案名
	 */
	public boolean downloadFile(String remoteFileName, String localFileName) {
		this.ftpClient.enterLocalPassiveMode();// 被動模式連接配接
		BufferedOutputStream buffOut = null;
		try {
			buffOut = new BufferedOutputStream(new FileOutputStream(
					localFileName));
			boolean ifDownload = ftpClient
					.retrieveFile(remoteFileName, buffOut);
			if (!ifDownload) {
				System.out.println("下載下傳檔案失敗。。。");
				return false;
			} else {
				System.out.println("下載下傳檔案成功。。。");
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		} finally {
			try {
				if (buffOut != null)
					buffOut.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return true;
	}

	/**
	 * 關閉FTP連接配接
	 */
	public void close() {
		try {
			if (ftpClient != null) {
				ftpClient.logout();
				ftpClient.disconnect();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 冒泡排序字元串(從大到小)
	 */
	public String[] sort(String[] str_Array) {
		if (str_Array == null) {
			throw new NullPointerException("The str_Array can not be null!");
		}
		String tmp = "";
		for (int i = 0; i < str_Array.length; i++) {
			for (int j = 0; j < str_Array.length - i - 1; j++) {
				if (str_Array[j].compareTo(str_Array[j + 1]) < 0) {
					tmp = str_Array[j];
					str_Array[j] = str_Array[j + 1];
					str_Array[j + 1] = tmp;
				}
			}
		}
		return str_Array;
	}

	public static void main(String[] strs) {
		FtpUtil2 FtpUtil2 = new FtpUtil2("192.168.1.112", 20011, "test1",
				"test1", "/", true);
		FtpUtil2.downloadFile("test.txt", "d:\\test.txt");
	}

}
           

下面是ZIP工具類:

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.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.IOUtils;

import com.ibatis.common.logging.Log;
import com.ibatis.common.logging.LogFactory;

public class ZipUtil {
    private static final Log log = LogFactory.getLog(ZipUtil.class);


    /**
     * 壓縮檔案
     * 
     * @param srcfile File[] 需要壓縮的檔案清單
     * @param zipfile File 壓縮後的檔案
     */
    public static OutputStream zipFiles(List<File> srcfile, OutputStream outputStream) {
        byte[] buf = new byte[1024];
        try {       	        	
            // Create the ZIP file
            ZipOutputStream out = new ZipOutputStream(outputStream);
            // Compress the files
            for (int i = 0; i < srcfile.size(); i++) {
                File file = srcfile.get(i);
                FileInputStream in = new FileInputStream(file);
                // Add ZIP entry to output stream.
                out.putNextEntry(new ZipEntry(file.getName()));
                
                // Transfer bytes from the file to the ZIP file
                int len;
                while ((len = in.read(buf)) > 0) {
                	//System.out.println(len+"==============");
                	
                    out.write(buf, 0, len);
                }
                // Complete the entry
                out.closeEntry();
                in.close();
            }
            // Complete the ZIP file
            out.close();
        } catch (IOException e) {
           log.error("ZipUtil zipFiles exception:"+e);
        }
        return outputStream;
    }

    
    /**
     * 壓縮檔案
     * 
     * @param srcfile File[] 需要壓縮的檔案清單
     * @param zipfile File 壓縮後的檔案
     */
    public static void zipFiles(List<File> srcfile, File zipfile) {
        byte[] buf = new byte[1024];
        try {
            // Create the ZIP file
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipfile));
            // Compress the files
            for (int i = 0; i < srcfile.size(); i++) {
                File file = srcfile.get(i);
                FileInputStream in = new FileInputStream(file);
                // Add ZIP entry to output stream.
                out.putNextEntry(new ZipEntry(file.getName()));
                // Transfer bytes from the file to the ZIP file
                int len;
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
                // Complete the entry
                out.closeEntry();
                in.close();
            }
            // Complete the ZIP file
            out.close();
        } catch (IOException e) {
           log.error("ZipUtil zipFiles exception:"+e);
        }
    }   
    
    
    /**
     * 壓縮檔案
     * srcfile:key:檔案名,value:檔案對應的輸入流
     * @param srcfile
     * @param zipfile 
     * @see
     */
    public static void zipByStream(Map<String,InputStream> srcfile, File zipfile) {
        try {
            // Create the ZIP file
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipfile));
            // Compress the files
            System.out.println(srcfile.entrySet().size());
            for (Map.Entry<String, InputStream> fileEntry:srcfile.entrySet()) {
                InputStream in = fileEntry.getValue();
                // Add ZIP entry to output stream.
                System.out.println(in.available());
                out.putNextEntry(new ZipEntry(fileEntry.getKey()));
                // Transfer bytes from the file to the ZIP file
                byte[] bytes=IOUtils.toByteArray(in);
                out.write(bytes);
                out.closeEntry();
                in.close();
            }
            // Complete the ZIP file
            out.close();
        } catch (IOException e) {
           log.error("ZipUtil zipFiles exception:"+e);
           System.out.println(e.getMessage());
        }
    } 
    
    
    /**
     * 壓縮檔案
     * srcfile:key:檔案名,value:檔案對應的位元組數組
     * @param srcfile
     * @param zipfile 
     * @see
     */
    public static void zipByArray(Map<String,byte[]> srcfile, File zipfile) {
        byte[] buf = new byte[1024];
        try {
            // Create the ZIP file
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipfile));
            // Compress the files
            System.out.println(srcfile.entrySet().size());
            for (Map.Entry<String, byte[]> fileEntry:srcfile.entrySet()) {
                //InputStream in = fileEntry.getValue();
                // Add ZIP entry to output stream.
                out.putNextEntry(new ZipEntry(fileEntry.getKey()));
                // Transfer bytes from the file to the ZIP file
                byte[] bytes=fileEntry.getValue();//IOUtils.toByteArray(in);
                out.write(bytes);
                out.closeEntry();
                //in.close();
            }
            // Complete the ZIP file
            out.close();
        } catch (IOException e) {
           log.error("ZipUtil zipFiles exception:"+e);
           System.out.println(e.getMessage());
        }
    } 
    
    /**
     * 解壓縮
     * 
     * @param zipfile File 需要解壓縮的檔案
     * @param descDir String 解壓後的目标目錄
     */
    public static void unZipFiles(File zipfile, String descDir) {
        try {
            // Open the ZIP file
            ZipFile zf = new ZipFile(zipfile);
            for (Enumeration entries = zf.entries(); entries.hasMoreElements();) {
                // Get the entry name
                ZipEntry entry = ((ZipEntry) entries.nextElement());
                String zipEntryName = entry.getName();
                InputStream in = zf.getInputStream(entry);
                // System.out.println(zipEntryName);
                OutputStream out = new FileOutputStream(descDir + zipEntryName);
                byte[] buf1 = new byte[1024];
                int len;
                while ((len = in.read(buf1)) > 0) {
                    out.write(buf1, 0, len);
                }
                // Close the file and stream
                in.close();
                out.close();
            }
        } catch (IOException e) {
            log.error("ZipUtil unZipFiles exception:"+e);
        }
    }

    /**
     * Main
     * 
     * @param args
     */
    public static void main(String[] args) {
    	List<File> srcfile=new ArrayList<File>();
    	srcfile.add(new File("d:\\1.jpg"));
    	srcfile.add(new File("d:\\2.jpg"));
    	srcfile.add(new File("d:\\3.jpg"));
    	srcfile.add(new File("d:\\4.jpg"));
        File zipfile = new File("d:\\pic.zip");
        ZipUtil.zipFiles(srcfile, zipfile);
    }
}
           

繼續閱讀