2020久久超碰欧美精品最新亚洲欧美日韩久久精品,国产福利电影一区二区三区,亚洲欧美日韩一区在线观看,亚洲国产欧美日韩欧美特级,亚洲欧美日韩成人一区久久,欧美日韩精品一区二区三区不卡,国产欧美日韩va另类影音先锋,亚洲欧美日韩久久精品,亚洲欧美日韩国产成人精品影院,亚洲国产欧美日韩精品一区二区三区,欧美日韩国产成人高清视频,日韩久久精品国产免费观看频道,久久人人爽人人爽从片av高清,国产精品综合一区二区

首頁技術文章正文

Java培訓:基于Http的文件斷點續傳實現

更新時間:2022-11-10 來源:黑馬程序員 瀏覽量:

IT培訓班

       1.斷點續傳的介紹

  客戶端軟件斷點續傳指的是在下載或上傳時,將下載或上傳任務(一個文件或一個壓縮包)人為的劃分為幾個部分,每一個部分采用一個線程進行上傳或下載,如果碰到網絡故障,可以從已經上傳或下載的部分開始繼續上傳下載未完成的部分,而沒有必要從頭開始上傳下載。從而達到讓用戶節省時間,提高速度的目的。

  2.斷點續傳的環境要求

  (1). 如果是基于http請求與響應實現的斷點續傳,需要服務器支持"響應一部分"數據的功能;(本案例采用的是tomcat7服務器,而tomcat7服務器是支持這個功能的)

  (2). 在客戶端需要使用RandomAccessFile類對文件任意位置的數據進行隨機讀寫操作;

  3.java的RandomAccessFile類介紹

  java的API中對RandomAccessFile類的解釋如下:

1668496450759_1.jpg

  

  我對RandomAccessFile類的理解是:RandomAccessFile類是java提供的一個可以用于隨機讀寫文件內容的類,我們可以對RandomAccessFile類關聯的文件中的任意位置和任意大小的數據進行任意的讀寫操作;因此要想完成文件的斷點續傳操作,該類的使用是必不可少的!

  4.斷點續傳的基本實現思路

  

1668060353422_2.jpg

  5.斷點續傳的代碼實現

  基礎環境搭建:

  (1). 創建WEB的maven工程;

  (2). 引入maven的tomcat7插件;

  (3). 在webapp目錄下存放多個文件,以備測試斷點續傳下載使用;

  java客戶端代碼實現:

public class MyDownLoadClient {
    public static String urlpath = "http://127.0.0.1:80/";
    private static int threadCount = 5;

    public static void main(String[] args) throws Exception {
        // 讓用戶輸入要下載的文件名稱
        Scanner sc = new Scanner(System.in);
        System.out.println("請輸入要下載的文件名稱:");
        String file = sc.next();
        urlpath = urlpath.concat(file);
        // 獲取文件總大小
        URL url = new URL(urlpath);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(2000);
        int responseCode = conn.getResponseCode();
        if (responseCode == 200) {
            int contentLength = conn.getContentLength();
            System.out.println("length" + contentLength);
            int part = contentLength / threadCount;
            // 讀配置文件
            ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
            CountDownLatch count;
            InputStream in = MyDownLoadClient.class.getClassLoader().getResourceAsStream(file + ".properties");
            if (in != null) {
                // 說明該文件不是第一次下載,需要斷點續傳
                Properties p = new Properties();
                p.load(in);
                in.close();
                Set<String> keys = p.stringPropertyNames();
                count = new CountDownLatch(keys.size());
                for (String key : keys) {
                    String value = p.getProperty(key);
                    String[] arr = value.split(",");
                    long start = Long.parseLong(arr[0]);
                    long end = Long.parseLong(arr[1]);
                    map.put(key,value);
                    new DownloadThread(start, end, key, map, count, file).start();
                }
                p.clear();
                p = null;
            } else {
                count = new CountDownLatch(threadCount);
                // 說明該文件是第一次下載,直接下載即可
                for (int i = 0; i < threadCount; i++) {
                    long startIndex = i * part; //每個線程起始下載位置

                    long endIndex = (i + 1) * part;//每個線程的結束位置

                    if (i == threadCount - 1) {//最后一個線程的結束位置

                        endIndex = contentLength;

                    }
                    map.put( String.valueOf(i),startIndex+","+endIndex);
                    new DownloadThread(startIndex, endIndex, String.valueOf(i), map, count, file).start();
                }
            }
            // 等待任務完成,刪除配置文件
            count.await();

            new File(MyDownLoadClient.class.getClassLoader().getResource("").getPath(),file + ".properties").delete();
            System.out.println("==========================下載任務完成==========================");
        } else {
            System.out.println("連接服務器失敗...請檢查服務器是否暢通及資源路徑是否正確...");
        }
    }
}

  
      下載任務的線程代碼實現:

class DownloadThread extends Thread {
    private long startIndex;
    private long endIndex;
    private String threadId;
    private ConcurrentHashMap<String, String> map;
    private CountDownLatch count;
    //private long subTotal = 0;
    private String fileName;

    public DownloadThread(long startIndex, long endIndex, String threadId, ConcurrentHashMap<String, String> map, CountDownLatch count, String fileName) {
        this.startIndex = startIndex;
        this.endIndex = endIndex;
        this.threadId = threadId;
        this.map = map;
        this.count = count;
        this.fileName = fileName;
    }

    @Override
    public void run() {
        try {
            URL url = new URL(MyDownLoadClient.urlpath);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setRequestMethod("GET");

            conn.setConnectTimeout(5000);
            //固定寫法,表示向服務器請求部分資源
            conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);

            int responseCode = conn.getResponseCode();
            //狀態碼206表示請求部分資源成功
            if (responseCode == 206) {
                RandomAccessFile rafAccessFile = new RandomAccessFile(fileName, "rw");
                rafAccessFile.seek(startIndex);
                InputStream is = conn.getInputStream();
                int len = -1;
                byte[] buffer = new byte[1024];
                Random r = new Random();
                while ((len = is.read(buffer)) != -1) {
                    FileOutputStream fout = new FileOutputStream(this.getClass().getClassLoader().getResource("").getPath()+"\\"+fileName + ".properties");
                    try {
                        //模擬意外情況導致下載中斷的代碼

                        /*if (r.nextInt(2) == 0) {
                            int i = 1 / 0;
                        }*/
                        rafAccessFile.write(buffer, 0, len);
                        startIndex += len;
                        map.put(threadId, startIndex + "," + endIndex);
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    } finally {
                        Set<Map.Entry<String, String>> entries = map.entrySet();
                        for (Map.Entry<String, String> entry : entries) {
                            fout.write((entry.getKey() + "=" + entry.getValue() + "\r\n").getBytes());
                        }
                        fout.close();
                    }
                }
                rafAccessFile.close();
                System.out.println("線程" + threadId + "下載完成");
                System.gc();
            }
            count.countDown();
        } catch (Exception e) {
            e.printStackTrace();
            System.gc();
        }
    }
}

  
       6:功能測試

  (1). 在web工程中提前準備好要下載的文件;(任意類型,任意文件均可,本項目以三個api舉例)

1668060489927_3.jpg

  (2). 啟動tomcat服務器;(已經設置虛擬目錄為 "/" 端口號為 "80")

1668060511872_4.jpg

  (3). 啟動java主程序類(MyDownLoadClient),輸入要下載的文件名;

1668060527198_5.jpg

  (4). 可以通過打開線程任務中模擬意外情況的代碼,讓下載出現意外,當程序出現意外后,配置文件不會刪除,且會記錄下所有線程已經完成的下載量,以便于下次執行下載任務的時候,可以在此基礎上繼續完成下載任務;

  

1668060550550_6.jpg

  

1668060563679_7.jpg

  

1668060581279_8.jpg

  (5). 關閉模擬意外的代碼,重新執行程序,直到文件順利下載完成,程序會自動刪除對應的配置文件;

  

1668060606596_9.jpg

  7.功能實現總結

  斷點續傳最核心的思想就是利用RandomAccessFile類將一個大文件配合多線程拆分成多個片段進行讀寫,最終將多個線程讀寫的結果再合并成1個大文件即可;

  8.源代碼參考

分享到:
在線咨詢 我要報名
和我們在線交談!