在http斷點續(xù)傳的過程中,主要有以下幾個方面要注意:
1定欧,新建一個temp文件渔呵,記錄斷點的位置,也就是上次下載的數(shù)量砍鸠。
2扩氢,采用RandomAccessFile來進行文件讀寫,RandomAccessFile相當于是一個文件輸入輸出流的結(jié)合爷辱。提供了一些在文件中操作位置的方法录豺,比如定位用的getFilePointer( ),在文件里移動用的seek( )饭弓,以及判斷文件大小的length( )双饥、skipBytes()跳過多少字節(jié)數(shù)。
斷點續(xù)傳的步驟主要是以下幾點:
1示启,判斷temp文件是否存在兢哭,存在就讀取進度,不存在就創(chuàng)建一個新的夫嗓。
2迟螺,將讀取的進度寫入請求頭重,就是設(shè)置請求的范圍舍咖,例如:
HttpUrlConnection中是如下設(shè)置:
conn.setRequestProperty("Range", "bytes=" + lastPostion + "-" + endposition)矩父;
OKHttp設(shè)置在Header中:
builder.addHeader("RANGE", "bytes=" + startIndex + "-" + (contentLength - 1));
3,寫入APK文件的時候排霉,同時將進度寫入temp文件窍株。完成的時候刪除temp文件,以及關(guān)閉RandomAccessFile攻柠。
while ((len = is.read(buffer)) != -1) {
randomAccessFile.write(buffer, 0, len);//寫入APK
downloadLength += len;
downloadInfo.setProgress(downloadLength);
cacheFile.seek(0);
cacheFile.write(String.valueOf(downloadLength).getBytes()); //記錄進度
}
下面是一個完整實例:
public static class newThreadDown extends Thread {
private String urlstr;
private long lastPostion;
private long endposition;
public newThreadDown(String urlstr, long endposition) {
this.urlstr = urlstr;
this.endposition = endposition;
}
@Override
public void run() {
HttpURLConnection conn = null;
try {
URL url = new URL(urlstr);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(10 * 1000);
conn.setRequestMethod("GET");
conn.setReadTimeout(10 * 1000);
long startposition = 0;
// 創(chuàng)建記錄緩存文件
File tempfile = new File("e:\\" + 1 + ".txt");
if (tempfile.exists()) {
InputStreamReader isr = new InputStreamReader(new FileInputStream(tempfile));
BufferedReader br = new BufferedReader(isr);
String lastStr = br.readLine();
lastPostion = Integer.parseInt(lastStr);
conn.setRequestProperty("Range", "bytes=" + lastPostion + "-" + endposition);
br.close();
} else {
tempFile.createNewFile();
lastPostion = startposition;
conn.setRequestProperty("Range", "bytes=" + lastPostion + "-" + endposition);
}
if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
System.out.println(206 + "請求成功");
InputStream is = conn.getInputStream();
RandomAccessFile accessFile = new RandomAccessFile(new File("e:\\" + path.substring(path.lastIndexOf("/") + 1)),
"rwd");
accessFile.seek(lastPostion);
randomAccessFile.seek(startIndex);
RandomAccessFile cacheFile = new RandomAccessFile(tempFile, "rwd");
System.out.println("開始位置" + lastPostion);
byte[] bt = new byte[1024 * 200];
int len = 0;
long total = 0;
while ((len = is.read(bt)) != -1) {
total += len;
accessFile.write(bt, 0, len);
long currentposition = startposition + total;
cacheFile.seek(0);
rf.write(String.valueOf(currentposition).getBytes());
rf.close();
}
System.out.println("下載完畢");
is.close();
accessFile.close();
}
} catch (Exception e) {
e.printStackTrace();
}
super.run();
}
}
以上實現(xiàn)的是單線程下載球订,當要實現(xiàn)多線程下載的時候,做如下改進:
1瑰钮,對下載的文件進行分塊冒滩,每個線程負責(zé)不同區(qū)塊的下載,主要設(shè)置為設(shè)置下載的請求范圍浪谴,設(shè)置文件的寫入范圍:
conn.setRequestProperty("Range", "bytes=" + dEntity.startLocation + "-" + dEntity.endLocation);
//創(chuàng)建可設(shè)置位置的文件
RandomAccessFile file = new RandomAccessFile(dEntity.tempFile, "rwd");
//設(shè)置每條線程寫入文件的位置
file.seek(dEntity.startLocation);
2开睡,對每個線程的tempFile多加一個字段,判斷該線程是否下載完苟耻,當所有線程都下載完的時候篇恒,才是整體下載完成。
參考文章:http://www.reibang.com/p/5b2e22c42467凶杖, 修正了原文中存在的BUG胁艰。