Java ftp斷點續傳

FtpTransFile類
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
 
/**
 * 在這個版本中但願作的是軟件的多線程下載
 * 如今也作了斷點下載 ,是否斷點下載的依據爲目標文件是否已經存在 ,無論是否從斷點出繼續下載,
 * 分的線程下載的文件都是從指定的位置開始下載的
 * 
 */
public class FtpTransFile {
 
    private static String fileName; // 要上傳或下載的文件的名字
    private static String path;// 臨時文件夾的目錄,用於存放多個線程下載的文件
    static long threadBlock = 100 * 1024 * 1024L;
 
    /**
     * 
     * @param path
     *            要上傳的本地文件路徑 如"C:/Users/repace/Desktop/zhangke1.txt";
     * @param server
     *            ftp服務器ip地址 192.168.242.133
     * @param userName
     *            登陸ftp的用戶名 test
     * @param password
     *            登陸ftp用戶名對應的密碼 123456
     */
    public static void fileUpload(String OStype, String path, String server,
            String userName, String password) { // 要上傳的文件的本地路徑路徑
        // 目前可完成單個文件的上傳
 
        if (!(OStype.equalsIgnoreCase("windows") || OStype
                .equalsIgnoreCase("linux"))) {
            System.out.println("操做系統類型輸入錯誤,應爲windows或linux");
            return;
        }
 
        FTPClient ftpClient = new FTPClient();
        ftpClient.enterLocalPassiveMode(); // 這一句話必定要記得加上
        FileInputStream fis = null;
        try {
            ftpClient.connect(server);
            ftpClient.login(userName, password);
 
            File srcFile = new File(path);// 要上傳的本地文件路徑
            fis = new FileInputStream(srcFile);
            String storeName = srcFile.getName();// 要存儲的文件的名字
            String remoteFilename = "/mnt/data/ftp/www/" + OStype.toLowerCase() + "/"
                    + storeName;
            ftpClient.changeWorkingDirectory("/mnt/data/ftp/www/" + OStype.toLowerCase()
                    + "/"); // 設置上傳的文件在centos上的目錄,文件上傳不成功是要查看指定目錄的權限
            ftpClient.setBufferSize(1024);
            ftpClient.setControlEncoding("UTF-8");
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);// 設置文件類型(二進制)
            FTPFile[] files = ftpClient.listFiles(remoteFilename);// 判斷軟件中心是否包含這個文件
            if (files.length == 1) {// 軟件中心包含該文件
                long remoteSize = files[0].getSize();// 軟件中心的文件大小
                long localSize = srcFile.length();// 打算要上傳的文件大小
                if (remoteSize == localSize) { // 軟件中心有這個文件,而且和打算要上傳的文件大小同樣,則說要上傳的文件已存在
                    System.out.println("要上傳的文件已存在");
                    ftpClient.disconnect();
                    return;
                } else if (remoteSize > localSize) {// 軟件中心的文件比要上傳的大,可能新上傳的文件被修改了,而後再次上傳的
                    System.out.println("軟件中心的軟件比即將上傳的要大,無須上傳或從新命名要上傳的文件名");
                    ftpClient.disconnect();
                    return;
                }
                // 軟件中心存的文件比要上傳的文件小,則嘗試移動文件內讀取指針,實現斷點續傳 **************
                if (fis.skip(remoteSize) == remoteSize) {
                    ftpClient.setRestartOffset(remoteSize);
                    boolean i = ftpClient.storeFile(
                            new String(storeName.getBytes("UTF-8"),
                                    "iso-8859-1"), fis);
                    if (i) {
                        System.out.println("文件斷點續傳成功");
                        ftpClient.disconnect();
                        return;
                    }
                }
            } else { // 軟件中心不包含要上傳的文件,或者續傳不成功,則上傳全新的文件便可
                boolean i = ftpClient.storeFile(
                        new String(storeName.getBytes("UTF-8"), "iso-8859-1"),
                        fis);
                System.out.println("文件上傳" + i);
            }
 
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("FTP客戶端出錯!", e);
        } finally {
            try {
                fis.close();
                ftpClient.disconnect();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                throw new RuntimeException("關閉FTP鏈接發生異常!", e);
            }
        }
    }
 
    /**
     * *
     * 
     * @param OStype
     *            操做系統的類型 windows或者是linux
     * @param fileName
     *            指出要下載的文件名字 加後綴的
     * @param storePath
     *            下載以後想要在本地的存儲路徑,在window系統中支持兩種文件路徑\\ 或者/
     * @param server
     *            ftp服務器IP地址
     * @param userName
     *            ftp分配的登陸名 test
     * @param password
     *            與登陸名對應的登陸密碼 123456
     * @throws FileNotFoundException
     * @throws InterruptedException
     */
    public static void fileDownload(String OStype, String fileNames,
            String storePath, String server, String userName, String password)
            throws FileNotFoundException, InterruptedException { // 參數是帶後綴的文件名字和下載以後要存儲的本地路徑
        // 可完成單個文件的下載 ,
 
        if (!(OStype.equalsIgnoreCase("windows") || OStype
                .equalsIgnoreCase("linux"))) {
            System.out.println("操做系統類型輸入錯誤,應爲windows或linux");
            return;
        }
        fileName = fileNames;
 
        File file = new File(storePath);
        if (!file.exists()) {// 判斷文件夾是否存在,若是不存在則建立文件夾
            file.mkdir();
        }
 
        FTPClient ftpClient = new FTPClient();
        ftpClient.enterLocalPassiveMode(); // 這一句話必定要記得加上
        String remoteFileName = "/mnt/data/ftp/www/" + OStype.toLowerCase() + "/"
                + fileName; // 服務器上的文件,前面是文件夾的名字,後面的是文件的名字
        String localFileName = "";// 本地要存儲的文件絕對路徑 文件夾加上文件名
 
        try {
            ftpClient.connect(server);
            ftpClient.login(userName, password);
            ftpClient.setBufferSize(1024);
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 設置文件類型(二進制)
            FTPFile[] files = ftpClient.listFiles(remoteFileName);
            if (files.length == 0) { // 判斷軟件中心是否有要下載的軟件
                System.out.println("軟件中心沒有找到要下載的軟件");
                ftpClient.disconnect();
                return;
            } else { //軟件中心包含請求下載的文件
 
                long localSize = 0L; // 記錄本地文件的大小
                if (storePath.endsWith("\\") || storePath.endsWith("/"))// 存儲路徑直接是某個盤下的根目錄或者用戶加上了最後的斜線
                {
                    localFileName = storePath + fileName;
                    path = storePath
                            + fileName.substring(0, fileName.indexOf("."))
                            + "Temp/";
                } else {
                    localFileName = storePath + "/" + fileName;
                    path = storePath + "/"
                            + fileName.substring(0, fileName.indexOf("."))
                            + "Temp/";
                }
 
                File localFile = new File(localFileName);
                long remoteSize = files[0].getSize();// 軟件中心的文件大小
                if (localFile.exists()) {// 指定下載的文件在本地文件夾內已經存在
                    localSize = localFile.length();// 已存在的文件大小
                    if (remoteSize == localSize) {
                        System.out.println("文件已下載過,無需再下載");
                        return;
                    } else if (remoteSize > localSize) { // 以前下載未完成,實現斷點下載
                        System.out.println("斷點下載。。。");
                    }
                    if (remoteSize < localSize) {// 若是本地的文件比軟件中心的文件大,則說明本地的文件可能有錯,刪除,而後從頭開始下載
                        localFile.delete();
                        System.out.println("軟件重新開始下載");
                        localSize = 0L;
                    }
                } else {// 指定下載的文件在本地文件夾內不存在,從頭下載文件
                    localSize = 0L;
                    System.out.println("軟件從頭下載");
                }
 
                File tempfile = new File(path);
                if (tempfile.exists()) {// 判斷文件夾是否存在,若是已經存在,則刪除該文件夾及其全部的子文件,以避免其包含的線程影響後面的下載過程
                    System.out.println("delete 以前的臨時文件夾");
                    deleteTempFile(path);
                }
                tempfile.mkdir();// 新建存放臨時文件夾的目錄
 
                ExecutorService exec = Executors.newCachedThreadPool(); // 開始啓動多線程下載文件
                int threadNum = (int) ((remoteSize - localSize) / threadBlock + 1);// 每100M分一個線程下載
                                                                                    // 計算線程總數
                System.out.println("分紅的線程個數" + threadNum);
                CountDownLatch latch = new CountDownLatch(threadNum);
                System.out.println(fileNames + "請求還需下載的文件大小"
                        + (remoteSize - localSize));
                long[] startPos = new long[threadNum];
                ChildThread[] childThreads = new ChildThread[threadNum];// ChildThread
                                                                            // 變成ChildThread1共有4處修改
                for (int i = 0; i < threadNum; i++) {
                    startPos[i] = localSize + i * threadBlock; // 設置每一個線程開始下載文件的起始位置
 
                    childThreads[i] = new ChildThread(OStype, fileName,
                            storePath, server, userName, password, startPos[i],
                            i, latch); // 建立線程 線程編號從0開始
                    exec.execute(childThreads[i]);// 開始執行線程
                }
 
                latch.await(); // 等待全部的線程都運行結束
                exec.shutdown();
                tempFileToTargetFile(localFileName, childThreads, threadNum);// 把臨時獲得的文件夾內的文件合併到目標文件
 
            }
 
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("FTP客戶端出錯!", e);
        } finally {
            try {
                ftpClient.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException("關閉FTP鏈接發生異常!", e);
            }
        }
    }
 
    /**
     * @author repace 把臨時文件夾內的文件都寫入目標文件內 即將各個線程所下的文件進行合併
     * @param target
     *            目標文件 是以前用戶發送的請求要把請求下載的文件存放的絕對路徑的目錄
     *            好比要下載的是test.txt文件,想存在c:\\123\\文件夾內 則目標文件target
     *            指的的就是c:\\123\\test.txt
     * @param tempFile
     *            臨時文件夾的目錄則是 c:\\123\\testTemp\\
     * @param threadNum
     * @return
     * @throws IOException
     */
 
    public static boolean tempFileToTargetFile(String target,
            ChildThread[] childThreads, int threadNum) throws IOException { // 完成把臨時文件夾內的日誌都寫到目標文件中
 
        System.out.println("KAISHI HEBING");
        boolean result = true;
 
        FileInputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(target, true); // 追加內容
            for (int i = 0; i < threadNum; i++) { // 遍歷全部子線程建立的臨時文件,按順序把下載內容寫入目標文件中
                inputStream = new FileInputStream(
                        childThreads[i].localTempFileName);
                int len = 0;
                byte[] b = new byte[1024];
                int count = 0;
                while ((len = inputStream.read(b)) != -1) {
                    outputStream.write(b, 0, len);
                    outputStream.flush();
                    count += len;
                }
                inputStream.close();
 
            }
            outputStream.flush();
            outputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (outputStream != null) {
            outputStream.close();
        }
        File file = new File(target);
        System.out.print(target + "下載獲得的文件大小是 " + file.length());
        deleteTempFile(path);// 刪除臨時文件夾
        return result;
    }
 
    public static void deleteTempFile(String Path) {//刪除臨時文件夾
 
        File file = new File(Path);
        if (file.isFile()) {// 表示該文件不是文件夾
            file.delete();
        } else {
            // 首先獲得當前的路徑
            String[] childFilePaths = file.list();
            for (String childFilePath : childFilePaths) {
                File childFile = new File(file.getAbsolutePath() + "/"
                        + childFilePath);
                String s = childFile.getAbsolutePath();
                deleteTempFile(s);
            }
            file.delete();
        }
    }
 
}
ChildThread類
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
 
import org.apache.commons.net.ftp.FTPClient;
 
public class ChildThread extends Thread {
 
 
    public int id;
    private long startPosition;
    CountDownLatch latch;
 
    String remoteFileName;  //要下載的文件在軟件中心的文件
    String localTempFileName;   //用於存放每一個線程下載的臨時文件的絕對路徑  (帶上臨時文件的名字和後綴)
    String path;//臨時文件夾的目錄
 
    FTPClient ftpClient = new FTPClient();
 
    public ChildThread(String OStype,String fileName, String storePath,
            String server, String userName, String password,long startPos,int id,CountDownLatch latch) {
 
        ftpClient.enterLocalPassiveMode(); // 這一句話必定要記得加上
        remoteFileName = "/mnt/data/ftp/www/"+OStype.toLowerCase() +"/"+ fileName; // 服務器上的文件,前面是文件夾的名字,後面的是文件的名字
        startPosition=startPos;
        this.latch=latch;  
        this.id=id;
        try {
            ftpClient.connect(server);
            ftpClient.login(userName, password);
            ftpClient.setBufferSize(1024);
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);    // 設置文件類型(二進制)    
 
            if (storePath.endsWith("\\") || storePath.endsWith("/"))//給出的路徑下新建一個臨時文件夾,裏面存儲的是各個線程下載的文件
                {
                    localTempFileName=storePath +fileName.substring(0, fileName.indexOf("."))+"Temp/" +id+"_"+fileName;//保證臨時文件夾惟一 也應保證臨時文件的命名惟一
                } else{
                    localTempFileName=storePath + "/" +fileName.substring(0, fileName.indexOf("."))+"Temp/" + id+"_"+fileName;
                }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("FTP客戶端出錯!", e);
        } finally {
        }
    }
 
 
    public void run() {
 
        FileOutputStream outputStream = null;
            try {
                File threadTempFile=new File(localTempFileName);
                outputStream = new FileOutputStream(localTempFileName,true);
                ftpClient.setRestartOffset(startPosition+threadTempFile.length());  //設置每一個線程開始的下載位置  若是以前threadTempFile.length()不等於0,則從上次那個地方繼續下載  斷點下載
 
                InputStream in= ftpClient.retrieveFileStream(remoteFileName);
                int len = 0;
                byte[] b = new byte[1024];
                long count=threadTempFile.length();
                while((len = in.read(b)) != -1) { 
                    count +=len;//記錄文件中的長度加上此次準備寫的長度
                    if (count > FtpTransFile.threadBlock) { //加上最後一次讀到的已經比規定的線程塊大,則只取前面一部分便可
                        int lastLen= (int) (FtpTransFile.threadBlock-threadTempFile.length());
                        outputStream.write(b, 0,lastLen);//方法write(b, off, len),b[off]是寫入的第一個字節和b[off+len-1]是寫的這個操做的最後一個字節。
                        outputStream.flush();
                        break;
                    }
                    outputStream.write(b, 0, len);
                    outputStream.flush();
                }                
                in.close();//關閉流
                File file=new File(localTempFileName);
                System.out.println("Thread file "+id+" "+file.length());
                outputStream.close();
                ftpClient.disconnect();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        latch.countDown();//每一個線程結束的時候,則總的線程數減1        
    }
 
 
 
}
相關文章
相關標籤/搜索