斷點續(xù)傳

Java 斷點續(xù)傳
(1)原理
在下載行為出現(xiàn)中斷的時候,記錄下中斷的位置信息,然后在下次行為開始的時候,直接從記錄的這個位置開始下載內(nèi)容,而不再從頭開始。
分為兩步:

當(dāng)“上傳(下載)的行為”出現(xiàn)中斷,我們需要記錄本次上傳(下載)的位置(position)。
當(dāng)“續(xù)”這一行為開始,我們直接跳轉(zhuǎn)到postion處繼續(xù)上傳(下載)的行為。
(2)代碼

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

public class Test {
// step1:首先,我們定義了一個變量position,記錄在發(fā)生中斷的時候,已完成讀寫的位置。(這是為了方便,實際來說肯定應(yīng)該講這個值存到文件或者數(shù)據(jù)庫等進行持久化)
    private static int position = -1;

    public static void main(String[] args) {
        // 源文件與目標(biāo)文件
        File sourceFile = new File("D:/", "test.txt");
        File targetFile = new File("E:/", "test.txt");
        // 輸入輸出流
        FileInputStream fis = null;
        FileOutputStream fos = null;
        // 數(shù)據(jù)緩沖區(qū)
        byte[] buf = new byte[1];

        try {
            fis = new FileInputStream(sourceFile);
            fos = new FileOutputStream(targetFile);
            // 數(shù)據(jù)讀寫
            while (fis.read(buf) != -1) {
                fos.write(buf);
// step2:然后在文件讀寫的while循環(huán)中,我們?nèi)ツM一個中斷行為的發(fā)生。這里是當(dāng)targetFile的文件長度為3個字節(jié)則模擬拋出一個我們自定義的異常。(我們可以想象為實際下載中,已經(jīng)上傳(下載)了”x”個字節(jié)的內(nèi)容,這個時候網(wǎng)絡(luò)中斷了,那么我們就在網(wǎng)絡(luò)中斷拋出的異常中將”x”記錄下來)。
                if (targetFile.length() == 3) {
                    position = 3;
                    throw new FileAccessException();
                }
            }
        } catch (FileAccessException e) {
//step3:開啟”續(xù)傳“行為,即keepGoing方法.
            keepGoing(sourceFile,targetFile, position);
        } catch (FileNotFoundException e) {
            System.out.println("指定文件不存在");
        } catch (IOException e) {
            // TODO: handle exception
        } finally {
            try {
                // 關(guān)閉輸入輸出流
                if (fis != null)
                    fis.close();

                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    private static void keepGoing(File source,File target, int position) {
// step3.1:我們起頭讓線程休眠10秒鐘,這正是為了讓我們運行程序看到效果。     
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
// step3.2:在“續(xù)傳”行為開始后,通過RandomAccessFile類來包裝我們的文件,然后通過seek將指針指定到之前發(fā)生中斷的位置進行讀寫就搞定了。 
(實際的文件下載上傳,我們當(dāng)然需要將保存的中斷值上傳給服務(wù)器,這個方式通常為
        try {
            RandomAccessFile readFile = new RandomAccessFile(source, "rw");
            RandomAccessFile writeFile = new RandomAccessFile(target, "rw");
            readFile.seek(position);
            writeFile.seek(position);

            // 數(shù)據(jù)緩沖區(qū)
            byte[] buf = new byte[1];
            // 數(shù)據(jù)讀寫
            while (readFile.read(buf) != -1) {
                writeFile.write(buf);
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class FileAccessException extends Exception {

}

(3)實現(xiàn)結(jié)果
運行程序,那么文件就會開啟“由D盤上傳到E盤的過程”,我們首先點開E盤,會發(fā)現(xiàn)的確多了一個test.txt文件,打開它發(fā)現(xiàn)內(nèi)容如下:

在這里插入圖片描述

這個時候我們發(fā)現(xiàn)內(nèi)容只有“abc”。這是在我們預(yù)料以內(nèi)的,因為我們的程序模擬在文件上傳了3個字節(jié)的時候發(fā)生了中斷。
等待10秒鐘過去,然后再點開該文件,發(fā)現(xiàn)內(nèi)容的確已經(jīng)變成了“abc”,由此也就完成了續(xù)傳。


在這里插入圖片描述

android中的實現(xiàn)

 import...;
  2 
  3 public class MainActivity extends AppCompatActivity {
  4 
  5     private EditText et_path;
  6     private EditText et_threadCount;
  7     private LinearLayout ll_pb;
  8     private String path;
  9 
 10     private static int runningThread;// 代表正在運行的線程
 11     private int threadCount;
 12     private List<ProgressBar> pbList;//集合存儲進度條的引用
 13 
 14     @Override
 15     protected void onCreate(Bundle savedInstanceState) {
 16         super.onCreate(savedInstanceState);
 17         setContentView(R.layout.activity_main);
 18 
 19         et_path = findViewById(R.id.et_path);
 20         et_threadCount = findViewById(R.id.et_threadCount);
 21         ll_pb = findViewById(R.id.ll_pb);
 22         //添加一個進度條的引用
 23         pbList = new ArrayList<ProgressBar>();
 24     }
 25 
 26     //點擊按鈕實現(xiàn)下載邏輯
 27     public void click(View view) {
 28         //獲取下載路徑
 29         path = et_path.getText().toString().trim();
 30         //獲取線程數(shù)量
 31         String threadCounts = et_threadCount.getText().toString().trim();
 32         //移除以前的進度條添加新的進度條
 33         ll_pb.removeAllViews();
 34         threadCount = Integer.parseInt(threadCounts);
 35         pbList.clear();
 36         for (int i = 0; i < threadCount; i++) {
 37             ProgressBar v = (ProgressBar) View.inflate(getApplicationContext(), R.layout.layout, null);
 38 
 39             //把v添加到幾何中
 40             pbList.add(v);
 41 
 42             //動態(tài)獲取進度條
 43             ll_pb.addView(v);
 44         }
 45 
 46         //java邏輯移植
 47         new Thread() {
 48             @Override
 49             public void run() {
 50                 /*************/
 51                 System.out.println("你好");
 52                 try {
 53                     URL url = new URL(path);
 54                     HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 55                     conn.setRequestMethod("GET");
 56                     conn.setConnectTimeout(5000);
 57                     int code = conn.getResponseCode();
 58                     if (code == 200) {
 59                         int length = conn.getContentLength();
 60                         // 把運行線程的數(shù)量賦值給runningThread
 61                         runningThread = threadCount;
 62 
 63                         System.out.println("length=" + length);
 64                         // 創(chuàng)建一個和服務(wù)器的文件一樣大小的文件,提前申請空間
 65                         RandomAccessFile randomAccessFile = new RandomAccessFile(getFileName(path), "rw");
 66                         randomAccessFile.setLength(length);
 67                         // 算出每個線程下載的大小
 68                         int blockSize = length / threadCount;
 69                         // 計算每個線程下載的開始位置和結(jié)束位置
 70                         for (int i = 0; i < length; i++) {
 71                             int startIndex = i * blockSize;// 開始位置
 72                             int endIndex = (i + 1) * blockSize;// 結(jié)束位置
 73                             // 特殊情況就是最后一個線程
 74                             if (i == threadCount - 1) {
 75                                 // 說明是最后一個線程
 76                                 endIndex = length - 1;
 77                             }
 78                             // 開啟線程去服務(wù)器下載
 79                             DownLoadThread downLoadThread = new DownLoadThread(startIndex, endIndex, i);
 80                             downLoadThread.start();
 81 
 82                         }
 83 
 84                     }
 85                 } catch (MalformedURLException e) {
 86                     // TODO Auto-generated catch block
 87                     e.printStackTrace();
 88                 } catch (IOException e) {
 89                     // TODO Auto-generated catch block
 90                     e.printStackTrace();
 91                 }
 92                 /*************/
 93             }
 94         }.start();
 95 
 96     }
 97 
 98     private class DownLoadThread extends Thread {
 99         // 通過構(gòu)造方法吧每個線程的開始位置和結(jié)束位置傳進來
100         private int startIndex;
101         private int endIndex;
102         private int threadID;
103         private int PbMaxSize;//代表當(dāng)前下載(進度條)的最大值
104         private int pblastPosition;//如果中斷過,這是進度條上次的位置
105 
106         public DownLoadThread(int startIndex, int endIndex, int threadID) {
107             this.startIndex = startIndex;
108             this.endIndex = endIndex;
109             this.threadID = threadID;
110 
111         }
112 
113         @Override
114         public void run() {
115             // 實現(xiàn)去服務(wù)器下載文件
116             try {
117                 //計算進度條最大值
118                 PbMaxSize = endIndex - startIndex;
119                 URL url = new URL(path);
120                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
121                 conn.setRequestMethod("GET");
122                 conn.setConnectTimeout(5000);
123                 // 如果中間斷過,接著上次的位置繼續(xù)下載,聰慧文件中讀取上次下載的位置
124                 File file = new File(getFileName(path) + threadID + ".txt");
125                 if (file.exists() && file.length() > 0) {
126                     FileInputStream fis = new FileInputStream(file);
127                     BufferedReader bufr = new BufferedReader(new InputStreamReader(fis));
128                     String lastPosition = bufr.readLine();
129                     int lastPosition1 = Integer.parseInt(lastPosition);
130 
131                     //賦值給進度條位置
132                     pblastPosition = lastPosition1 - startIndex;
133                     // 改變一下startIndex的值
134                     startIndex = lastPosition1 + 1;
135                     System.out.println("線程id:" + threadID + "真實下載的位置:" + lastPosition + "-------" + endIndex);
136 
137                     bufr.close();
138                     fis.close();
139 
140                 }
141 
142                 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
143                 int code = conn.getResponseCode();
144                 if (code == 206) {
145                     // 隨機讀寫文件對象
146                     RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rw");
147                     // 每個線程從自己的位置開始寫
148 
149                     raf.seek(startIndex);
150                     InputStream in = conn.getInputStream();
151                     // 把數(shù)據(jù)寫到文件中
152                     int len = -1;
153                     byte[] buffer = new byte[1024];
154                     int totle = 0;// 代表當(dāng)前線程下載的大小
155                     while ((len = in.read(buffer)) != -1) {
156                         raf.write(buffer, 0, len);
157                         totle += len;
158 
159                         // 實現(xiàn)斷點續(xù)傳就是把當(dāng)前線程下載的位置保存起來,下次再下載的時候按照上次下載的位置繼續(xù)下載
160                         int currentThreadPosition = startIndex + totle;// 存到一個txt文本中
161                         // 用來存儲當(dāng)前線程當(dāng)前下載的位置
162                         RandomAccessFile raff = new RandomAccessFile(getFileName(path) + threadID + ".txt", "rwd");
163                         raff.write(String.valueOf(currentThreadPosition).getBytes());
164                         raff.close();
165 
166                         //設(shè)置進度條當(dāng)前的進度
167                         pbList.get(threadID).setMax(PbMaxSize);
168                         pbList.get(threadID).setProgress(pblastPosition + totle);
169                     }
170                     raf.close();
171                     System.out.println("線程ID:" + threadID + "下載完成");
172                     // 將產(chǎn)生的txt文件刪除,每個線程下載完成的具體時間不知道
173                     synchronized (DownLoadThread.class) {
174                         runningThread--;
175                         if (runningThread == 0) {
176                             //說明線程執(zhí)行完畢
177                             for (int i = 0; i < threadCount; i++) {
178 
179                                 File filedel = new File(getFileName(path) + i + ".txt");
180                                 filedel.delete();
181                             }
182 
183                         }
184 
185                     }
186 
187                 }
188             } catch (MalformedURLException e) {
189                 // TODO Auto-generated catch block
190                 e.printStackTrace();
191             } catch (IOException e) {
192                 // TODO Auto-generated catch block
193                 e.printStackTrace();
194             }
195 
196         }
197     }
198 
199     public String getFileName(String path) {
200         int start = path.lastIndexOf("/") + 1;
201         String subString = path.substring(start);
202         String fileName = "/data/data/com.lgqrlchinese.heima76android_11_mutildownload/" + subString;
203         return fileName;
204 
205     }
206 }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容