1. 程式人生 > >Java Http斷點續傳(下載)

Java Http斷點續傳(下載)

package **********;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
 * Encode:UTF-8
 * 
 * Author:GFC
 * 
 * 根據輸入的url和設定的執行緒數,來完成斷點續傳功能。
 * 
 * 每個執行緒支負責某一小段的資料下載;再通過RandomAccessFile完成資料的整合。
 */




public class MultiTheradDownLoad {
	private String urlStr = null;
    private String filename = null;
    private String tmpfilename = null;

    private int threadNum = 0;

    private CountDownLatch latch = null;//設定一個計數器,程式碼內主要用來完成對快取檔案的刪除

    private long fileLength = 0l;
    private long threadLength = 0l;
    private long[] startPos;//保留每個執行緒下載資料的起始位置。
    private long[] endPos;//保留每個執行緒下載資料的截止位置。

    private boolean bool = false;

    private URL url = null;

    //有參建構函式,先構造需要的資料
    public MultiTheradDownLoad(String urlStr, int threadNum) {
        this.urlStr = urlStr;
        this.threadNum = threadNum;
        startPos = new long[this.threadNum];
        endPos = new long[this.threadNum];
        latch = new CountDownLatch(this.threadNum);
    }

    /*
     * 組織斷點續傳功能的方法
     */
    public void downloadPart() {

        File file = null;
        File tmpfile = null;
        
    	//設定HTTP網路訪問代理
    	System.setProperty("http.proxySet", "true");  
   		System.setProperty("http.proxyHost", "proxy3.bj.petrochina");  
 		System.setProperty("http.proxyPort", "8080"); 


        //從檔案連結中獲取檔名,此處沒考慮檔名為空的情況,此種情況可能需使用UUID來生成一個唯一數來代表檔名。
        filename = urlStr.substring(urlStr.lastIndexOf('/') + 1, urlStr
                .contains("?") ? urlStr.lastIndexOf('?') : urlStr.length());
        tmpfilename = filename + "_tmp";

        try {
        	//建立url
            url = new URL(urlStr);
            
            //開啟下載連結,並且得到一個HttpURLConnection的一個物件httpcon
            HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
            httpcon.setRequestMethod("GET");
            
            //獲取請求資源的總長度。
            fileLength = httpcon.getContentLengthLong();

            //下載檔案和臨時檔案
            file = new File(filename);//相對目錄
            tmpfile = new File(tmpfilename);

            //每個執行緒需下載的資源大小;由於檔案大小不確定,為避免資料丟失
            threadLength = fileLength%threadNum == 0 ? fileLength/threadNum : fileLength/threadNum+1;
            //列印下載資訊
            System.out.println("fileName: " + filename + " ," + "fileLength= "
                    + fileLength + " the threadLength= " + threadLength);
            
            //各個執行緒在exec執行緒池中進行,起始位置--結束位置
            if (file.exists() && file.length() == fileLength) {
                System.out.println("檔案已存在!!");
                return;
            } else {
                setBreakPoint(startPos, endPos, tmpfile);
                ExecutorService exec = Executors.newCachedThreadPool();
                for (int i = 0; i < threadNum; i++) {
                    exec.execute(new DownLoadThread(startPos[i], endPos[i],
                            this, i, tmpfile, latch));
                }
                latch.await();//當你的計數器減為0之前,會在此處一直阻塞。
                exec.shutdown();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        //下載完成後,判斷檔案是否完整,並刪除臨時檔案
        if (file.length() == fileLength) {
            if (tmpfile.exists()) {
                System.out.println("刪除臨時檔案!!");
                tmpfile.delete();
            }
        }
    }

    /*
     * 斷點設定方法,當有臨時檔案時,直接在臨時檔案中讀取上次下載中斷時的斷點位置。沒有臨時檔案,即第一次下載時,重新設定斷點。
     * 
     * Rantmpfile.seek()跳轉到一個位置的目的是為了讓各個斷點儲存的位置儘量分開。
     * 
     * 這是實現斷點續傳的重要基礎。
     */
    private void setBreakPoint(long[] startPos, long[] endPos, File tmpfile) {
        RandomAccessFile rantmpfile = null;
        try {
            if (tmpfile.exists()) {
                System.out.println("繼續下載!!");
                rantmpfile = new RandomAccessFile(tmpfile, "rw");
                for (int i = 0; i < threadNum; i++) {
                    rantmpfile.seek(8 * i + 8);
                    startPos[i] = rantmpfile.readLong();

                    rantmpfile.seek(8 * (i + 1000) + 16);
                    endPos[i] = rantmpfile.readLong();

                    System.out.println("the Array content in the exit file: ");
                    System.out.println("thre thread" + (i + 1) + " startPos:"
                            + startPos[i] + ", endPos: " + endPos[i]);
                }
            } else {
                System.out.println("the tmpfile is not available!!");
                rantmpfile = new RandomAccessFile(tmpfile, "rw");
                
                //最後一個執行緒的截止位置大小為請求資源的大小
                for (int i = 0; i < threadNum; i++) {
                    startPos[i] = threadLength * i;
                    if (i == threadNum - 1) {
                        endPos[i] = fileLength;
                    } else {
                        endPos[i] = threadLength * (i + 1) - 1;
                    }

                    rantmpfile.seek(8 * i + 8);
                    rantmpfile.writeLong(startPos[i]);

                    rantmpfile.seek(8 * (i + 1000) + 16);
                    rantmpfile.writeLong(endPos[i]);

                    System.out.println("the Array content: ");
                    System.out.println("thre thread" + (i + 1) + " startPos:"
                            + startPos[i] + ", endPos: " + endPos[i]);
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (rantmpfile != null) {
                    rantmpfile.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    /*
     * 實現下載功能的內部類,通過讀取斷點來設定向伺服器請求的資料區間。
     */
    class DownLoadThread implements Runnable {

        private long startPos;
        private long endPos;
        private MultiTheradDownLoad task = null;
        private RandomAccessFile downloadfile = null;
        private int id;
        private File tmpfile = null;
        private RandomAccessFile rantmpfile = null;
        private CountDownLatch latch = null;

        public DownLoadThread(long startPos, long endPos,
                MultiTheradDownLoad task, int id, File tmpfile,
                CountDownLatch latch) {
            this.startPos = startPos;
            this.endPos = endPos;
            this.task = task;
            this.tmpfile = tmpfile;
            try {
            	//
                this.downloadfile = new RandomAccessFile(this.task.filename,"rw");
                this.rantmpfile = new RandomAccessFile(this.tmpfile, "rw");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            this.id = id;
            this.latch = latch;
        }

        @Override
        public void run() {

            HttpURLConnection httpcon = null;
            InputStream is = null;
            int length = 0;

            System.out.println("執行緒" + id + " 開始下載!!");

            while (true) {
                try {
                    httpcon = (HttpURLConnection) task.url.openConnection();
                    httpcon.setRequestMethod("GET");
                    
                    //防止網路阻塞,設定指定的超時時間;單位都是ms。超過指定時間,就會丟擲異常
                    httpcon.setReadTimeout(20000);//讀取資料的超時設定
                    httpcon.setConnectTimeout(20000);//連線的超時設定

                    if (startPos < endPos) {
                        
                        //向伺服器請求指定區間段的資料,這是實現斷點續傳的根本。
                        httpcon.setRequestProperty("Range", "bytes=" + startPos+ "-" + endPos);

                        System.out.println("執行緒 " + id+ " 長度:---- "+ (endPos - startPos));

                        downloadfile.seek(startPos);

                        if (httpcon.getResponseCode() != HttpURLConnection.HTTP_OK
                                && httpcon.getResponseCode() != HttpURLConnection.HTTP_PARTIAL) {
                            this.task.bool = true;
                            httpcon.disconnect();
                            downloadfile.close();
                            System.out.println("執行緒 ---" + id + " 下載完成!!");
                            latch.countDown();//計數器自減
                            break;
                        }

                        is = httpcon.getInputStream();//獲取伺服器返回的資源流
                        long count = 0l;
                        byte[] buf = new byte[1024];

                        while (!this.task.bool && (length = is.read(buf)) != -1) {
                            count += length;
                            downloadfile.write(buf, 0, length);
                            
                            //不斷更新每個執行緒下載資源的起始位置,並寫入臨時檔案;為斷點續傳做準備
                            startPos += length;
                            rantmpfile.seek(8 * id + 8);
                            rantmpfile.writeLong(startPos);
                        }
                        System.out.println("執行緒 " + id
                                + " 總下載大小: " + count);
                        
                        //關閉流
                        is.close();
                        httpcon.disconnect();
                        downloadfile.close();
                        rantmpfile.close();
                    }
                    latch.countDown();//計數器自減
                    System.out.println("執行緒 " + id + " 下載完成!!");
                    break;
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (is != null) {
                            is.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}

其中設定HTTP訪問代理在公網上不需要。可以看出來,整個類中分為三大部分:下載功能方法downloadPart();設定斷點的私有方法setBreakPoint(long[] startPos, long[] endPos, File tmpfile);以及實現下載功能的內部類,通過讀取斷點來設定向伺服器請求的資料區間class DownLoadThread,其中重寫了執行緒的run()方法。

相關推薦

Java Http斷點下載

package **********; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.Rando

HTTP 斷點分塊傳輸

簡述 斷點續傳:指的是在上傳/下載時,將任務(一個檔案或壓縮包)人為的劃分為幾個部分,每一個部分採用一個執行緒進行上傳/下載,如果碰到網路故障,可以從已經上傳/下載的部分開始繼續上傳/下載未完成的部分,而沒有必要從頭開始上傳/下載。可以節省時間,提高速度。

多執行緒斷點

一、 學習內容 1、 多檔案下載列表的顯示 2、 啟動多個執行緒分段下載 二、 多執行緒下載原理簡介 假設要分3個執行緒下載一個100位元組的檔案:從頭到尾,每個執行緒下載一段 三、 學習點 1、 Adapter的getCount() 2、 A

多執行緒斷點

一、 學習內容 1、 基本UI定義 2、 資料庫的操作 3、 Service的啟動 4、 Activity給service傳遞引數 5、 使用廣播回傳資料到Activity 6、 執行緒和Handler 7、 網路操作:檔案的寫入,網路往本地磁碟寫入 二、 網路下

【轉】文件下載斷點客戶端與服務端的實現

http協議 當前時間 end box [] ada demo 服務端 sem 【轉】文件下載之斷點續傳(客戶端與服務端的實現) 【轉】文件下載之斷點續傳(客戶端與服務端的實現) 前面講了文件的上傳,今天來聊聊文件的下載。 老規矩,還是從最簡單粗暴的開始。那麽多簡單算簡單

http range實現斷點斷點

使用http range實現斷點續傳(伺服器端): public void downRangeFile( File downloadFile, HttpServletResponse response,

HttpURLConnection之斷點多執行緒下載

public class MainActivity extends Activity { private static final int PROCESSING = 1; private static final int FAILURE = -1; private EditText pat

分享個C++封裝Libcurl程式碼支援下載檔案、GET\POST、重定向斷點等功能

前言 前面分享過一個Windows上封裝Winhttp和WinInet API的程式碼,結果下載頁好評特別多(呵呵),謝謝大家賞臉。文章地址:開源一個C++實現的簡單HTTP協議處理庫,裡面有程式碼資源下載地址。但是,在實際開發過程中我發現WinHttp API嚴重依賴微

Java斷點基於socket與RandomAccessFile的簡單實現

Java斷點續傳(基於socket與RandomAccessFile的簡單實現)   這是一個簡單的C/S架構,基本實現思路是將伺服器註冊至某個空閒埠用來監視並處理每個客戶端的傳輸請求。   客戶端先獲得使用者給予的需傳輸檔案與目標路徑,之後根據該檔案例項化RandomAccessFile為只讀,之後客戶

Java實現斷點 (HTTP)

斷點續傳的原理 其實斷點續傳的原理很簡單,就是在 Http 的請求上和一般的下載有所不同而已。 打個比方,瀏覽器請求伺服器上的一個文時,所發出的請求如下: 假設伺服器域名為 www.sjtu.edu.cn,檔名為 down.zip。 GET /down.zip HTTP/1

java實現檔案的斷點併發下載

說明 用java實現檔案的斷點續傳,使用了HTTP的首部欄位實現,在網上看到例子,手動實現一遍,理解其原理,在這記錄下 正文 要實現斷點續傳,要在請求中設定請求開始的位置和結束位置,在HTTP請求中設定RANGE首部欄位,之後伺服器如果能正常返回,返回

Java 實現斷點 (HTTP)

公司需要用Java做斷點續傳的實現,沒有接觸過,不過根據自己的理解就是檔案接著上次傳輸的繼續完成傳輸,具體的操作看到IBM這位仁兄的例子。 1、斷點續傳的原理 其實斷點續傳的原理很簡單,就是在 Http 的請求上和一般的下載有所不同而已。  打個比方,瀏覽器請求伺服

Java 實現斷點 (HTTP)

/* /* * SiteFileFetch.java */ package NetFox; import java.io.*; import java.net.*; public class SiteFileFetch extends Thread { Sit

FTP下用reget實現斷點 FTP下get顯示進度

同事在Linux通過FTP獲取一個1.3G的大檔案,傳了一個上午,結果在1.1G左右的時候,資料鏈路斷開,ftp命令假死狀態,檔案大小不在增加。如果重新匯入,下午肯定完不成了,得考慮斷點續傳,以前都是通過windows下的工具完成的,目前的問題是:在Unix下如何處理呢?方法

java實現檔案的斷點下載

java的斷點續傳是基於之前java檔案下載基礎上的功能拓展 首先設定一個以執行緒ID為名的下載進度檔案, 每一次下載的進度會儲存在這個檔案中,下一次下載的時候,會根據進度檔案裡面的內容來判斷下載的進度。 package com.ldw.multilthreaddown

http斷點與檔案下載原理解析

 一、斷點續傳的原理        其實斷點續傳的原理很簡單,就是在http的請求上和一般的下載有所不同而已。        打個比方,瀏覽器請求伺服器上的一個文時,所發出的請求如下: ? get /down.zip http/1.1 accept: image/gif, image/x-

Android 多線程斷點同時下載多個大文件

總結 orm acc ast mil view inpu lib 設置 最近學習在Android環境中一些網絡請求方面的知識,其中有一部分是關於網絡下載方面的知識。在這裏解析一下自己寫的demo,總結一下自己所學的知識。下圖為demo的效果圖,仿照一些應用下載商城在List

http斷點

http斷點續傳斷點續傳 從下載斷開的位置,繼續下載,直到下載完整 四個必須的HTTP頭域 Range用於請求頭中,指定第一個字節的位置和最後一個字節的位置,一般格式:Range:(unit=first byte pos)-[last byte pos] Range : bytes=50-

http斷點原理

位置 解壓 md5 完成 不存在 狀態 失敗 來看 ID 這周完成了一個斷點續傳的功能。 我們的遊戲裏加載地圖的邏輯簡化而言是這樣: 1.首先用本地的md5文件校驗地圖文件(很多文件)是否完整。(中間有很多步驟,任何步驟失敗都認為地圖不完整) 2.如果完整,直接加載地圖。

斷點下載原理分析

斷點續傳和下載原理分析 斷點續傳和斷點下載都是用的RandomAccessFile, 它具有移動指定的檔案大小的位置的功能seek 。 斷點續傳是由伺服器給客戶端一個已經上傳的位置標記position,然後客戶端再將檔案指標移動到相應的position,通過輸入流將檔案剩餘部分讀出來傳輸給伺