斷點續傳教學例子
package net.url;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
/**
* 斷點續傳java例子
*/
public class BreakpointResume {
/**
* 斷點續傳時每段的位元組數
*/
private static final long FRAGMENT_SIZE = 2048L;
/**
* 本地檔案儲存目錄
*/
private static final String LOCAL_PATH = "D:\Demo";
/**
* args的第一個引數指示Web資源的URL地址,協議型別限定為HTTP。required<br>
* 第二個引數指示本地檔案的絕對路徑 + 檔名。optional
*
* @param args
*/
public static void main(String[] args) throws Exception {
if (args.length != 1 && args.length != 2) {
System.out.println("Usage[1]:java BreakpointResume url localFile");
System.out.println("or Usage[2]:java BreakpointResume url");
return;
}
String localFile = null;
String url = args[0];
if (args.length == 2) {
localFile = args[1];
}
// 1. 校驗URL和本地檔案的格式
String checkURLMsg = checkURLFormat(url);
if (checkURLMsg != null) {
System.err.println(checkURLMsg);
return;
}
String checkLocalFileMsg = checkLocalFileFormat(localFile);
if (checkLocalFileMsg != null) {
System.err.println(checkLocalFileMsg);
return;
}
// 2. 計算Range的範圍
long startRange, endRange;
if (localFile == null) {
startRange = 0L;
endRange = FRAGMENT_SIZE;
} else {
startRange = new File(localFile).length();
endRange = startRange + FRAGMENT_SIZE;
}
// 3. 訪問網路資源然後分段下載
downloadPartially(url, localFile, startRange, endRange);
}
/**
* 分段下載。<br>
* <p>假設要下載的資源為,分段下載的思路如下:</p>
* <p>1) 設定請求屬性Range</p>
* <p>2) 連線遠端Web資源</p>
* <p>3) 校驗響應中的狀態行,如果不是200或者206,就停止傳輸</p>
* <p>4) 檢視.tmp檔案(03.jpg.tmp)是否已經存在,如果不存在,就新建該檔案</p>
* <p>5) 將響應內容寫入.tmp檔案</p>
* <p>6) 如果Web資源已經全部傳輸完了,將.tmp檔案的字尾去掉,還原為檔案本來的字尾和格式,然後結束while迴圈</p>
* <p>7) 如果Web資源沒有傳輸完,計算下一次傳輸的Range的範圍</p>
*
* @param url
* @param localFile
* @param startRange
* @param endRange
* @throws MalformedURLException 應該不會丟擲該異常,因為已經限定了只能用HTTP協議訪問Web資源,並且進行了校驗
*/
private static void downloadPartially(String url, String localFile, long startRange, long endRange) throws MalformedURLException {
long startTime = System.currentTimeMillis();
URL resource = new URL(url);
// 加入num是用來模擬Web資源傳輸了一部分,然後第二次傳輸時,從上次結束的部分開始獲取
int num = 0;
while (true) {
if (++num == 10) {
// break;
}
HttpURLConnection conn = null;
InputStream in = null;
RandomAccessFile raf = null;
try {
conn = (HttpURLConnection) resource.openConnection();
// 1) 設定請求屬性Range
conn.setRequestProperty("Range", "bytes=" + startRange + "-" + (endRange == -1L ? "" : endRange));
// 2) 連線遠端Web資源
conn.connect();
// 3) 校驗狀態行,如果不是成功或者部分內容,就停止傳輸
String statusLine = conn.getHeaderField(null);// 狀態行
System.out.println("statusLine=" + statusLine);
if (!statusLine.contains("200") && !statusLine.contains("206")) {
throw new Exception("獲取Web資源[" + url + "]時,響應狀態不是200或者206");
}
// 獲取資源長度
String cr = conn.getHeaderField("Content-Range");
if (cr == null || "".equals(cr.trim())) {
throw new Exception("獲取Web資源[" + url + "]時,響應資訊中Content-Range為null");
}
System.out.println("Content-Range=" + cr);
cr = cr.replace("[", "").replace("]", "").replace("bytes", "").trim();
// 解析響應訊息頭中Content-Range欄位的值
long resourceStartPos = Long.parseLong(cr.substring(0, cr.indexOf("-")));
long resourceEndPos = Long.parseLong(cr.substring(cr.indexOf("-") + 1, cr.indexOf("/")));
long resourceTotalLength = Long.parseLong(cr.substring(cr.indexOf("/") + 1));
System.out.println("resourceStartPos=" + resourceStartPos
+ ", resourceEndPos=" + resourceEndPos
+ ", resourceTotalLength=" + resourceTotalLength);
// 將相應內容讀取到buf中
byte[] buf = new byte[(int) (resourceEndPos - resourceStartPos)];
in = conn.getInputStream();
in.read(buf);
// 4) 檢視.tmp檔案是否已經存在,如果不存在,就新建該檔案
if (localFile == null) {
localFile = LOCAL_PATH + File.separator + url.substring(url.lastIndexOf("/") + 1) + ".tmp";
}
System.out.println("localFile=" + localFile);
File f = new File(localFile);
if (!f.exists()) {// .tmp檔案不存在,使用OutputStream手動建立該檔案
OutputStream os = new FileOutputStream(f);
try {os.close();} catch (Exception e) {}
}
TimeUnit.MILLISECONDS.sleep(10L);
raf = new RandomAccessFile(f, "rwd");
raf.seek(startRange);
raf.write(buf);
try {raf.close();} catch (Exception e) {}
// 5) 如果Web資源已經全部傳輸完了,將.tmp檔案的字尾去掉,還原為檔案本來的字尾和格式,然後結束while迴圈
// 從Web伺服器返回的內容中的位元組,是以0為索引開始計數的
if (resourceEndPos == resourceTotalLength - 1) {
f.renameTo(new File(LOCAL_PATH + File.separator + url.substring(url.lastIndexOf("/") + 1)));
break;
}
// 6) 如果Web資源沒有傳輸完,計算下一次傳輸的Range的範圍
// 如果剩下的要傳輸的內容不超過FRAGMENT_SIZE的1.5倍,就一起全部傳輸過來,減少HttpURLConnection連線帶來的資源消耗
if (resourceTotalLength - resourceEndPos <= FRAGMENT_SIZE * 3 / 2) {
startRange = resourceEndPos;
endRange = -1L;
} else {
startRange = resourceEndPos;
endRange = resourceEndPos + FRAGMENT_SIZE;
}
System.out.println("next startRange=" + startRange + ", endRange=" + endRange);
} catch (Exception e) {
System.err.println(e.getMessage());
return;
} finally {
if (in != null) {
try {in.close();} catch (Exception e) {}
}
if (conn != null) {
conn.disconnect();
}
}
}
long time = System.currentTimeMillis() - startTime;
System.out.println("傳輸Web資源[" + url + "]共耗時" + time / 1000 + "s" + time % 1000 + "ms");
}
/**
* URL格式限定如下:
* <p>
* URL的長度至少是20
* </p>
* <p>
* URL的協議型別必須是HTTP
* </p>
* <p>
* URL中必須包含".",且"."後面的字元個數不能超過10
* </p>
*
* @param url
* @return
*/
private static String checkURLFormat(String url) {
if (url.length() < 20) {
return "url的長度至少為20";
}
String protocol = url.substring(0, 7);
if (!protocol.equalsIgnoreCase("http://")) {
return "url必須以http://開頭(不區分大小寫)";
}
int dotIndex = url.lastIndexOf(".");
if (dotIndex == -1) {
return "url中必須有'.'";
}
String resourceSuffix = url.substring(dotIndex);
if (resourceSuffix.length() > 10) {
return "url格式不正確,資源名稱的字尾('.'後面的字元)不能超過10個字元";
}
return null;
}
/**
* 本地檔案格式限定如下:
* <p>
* 如果localFile不為null,那麼該檔案在本地必須存在並且是檔案
* </p>
* <p>
* 如果localFile不為null,那麼該檔案的字尾必須是.tmp,說明該檔案在之前沒有傳輸完,本次繼續傳輸
* </p>
*
* @param localFile
* @return
*/
private static String checkLocalFileFormat(String localFile) {
if (localFile == null) {
return null;
}
String retMsg = null;
try {
File f = new File(localFile);
if (!f.exists()) {
retMsg = "本地檔案[" + localFile + "]不存在";
} else if (!f.isFile()) {
retMsg = "本地檔案[" + localFile + "]不是一個檔案";
} else if (!localFile.endsWith(".tmp")) {
retMsg = "本地檔案[" + localFile + "]應該以.tmp結尾";
}
} catch (Exception e) {
retMsg = "在讀取本地檔案[" + localFile + "]時出錯,請檢查該檔案";
}
return retMsg;
}
}
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2894/viewspace-2823981/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 上傳——斷點續傳之理論篇斷點
- 上傳——斷點續傳之實踐篇斷點
- 12. 斷點續傳的原理斷點
- scp實現斷點續傳---rsync斷點
- 關於http斷點續傳那點事HTTP斷點
- Android斷點續傳下載器JarvisDownloaderAndroid斷點JAR
- Java實現檔案斷點續傳Java斷點
- 大檔案上傳、斷點續傳、秒傳、beego、vue斷點GoVue
- OSS網頁上傳和斷點續傳(STSToken篇)網頁斷點
- 1. 大檔案上傳如何斷點續傳斷點
- VUE-多檔案斷點續傳、秒傳、分片上傳Vue斷點
- 斷點續傳瞭解一下啊?斷點
- JAVA編寫的斷點續傳小程式Java斷點
- 使用Visual C#實現斷點續傳C#斷點
- 使用curl斷點續傳下載檔案斷點
- OSS網頁上傳和斷點續傳(OSS配置篇)網頁斷點
- OSS網頁上傳和斷點續傳(終結篇)網頁斷點
- JAVA實現大檔案分片上傳斷點續傳Java斷點
- 支援斷點續傳的大檔案傳輸協議斷點協議
- Android 中 Service+Notification 斷點續傳下載Android斷點
- Android中的多執行緒斷點續傳Android執行緒斷點
- Linux如何實現斷點續傳檔案功能?Linux斷點
- 資料壓縮傳輸與斷點續傳那些事兒斷點
- Android原生下載(上篇)基本邏輯+斷點續傳Android斷點
- http斷點續傳原理:http頭 Range、Content-RangeHTTP斷點
- node靜態伺服器斷點續傳實現伺服器斷點
- Linux如何遠端複製,限速和斷點續傳Linux斷點
- Node.js實現大檔案斷點續傳Node.js斷點
- vue+element+oss實現前端分片上傳和斷點續傳Vue前端斷點
- Redis主從複製斷點續傳的工作原理概述Redis斷點
- Range/Content-Range與斷點續傳,瞭解一下?斷點
- Android下載檔案(一)下載進度&斷點續傳Android斷點
- 上傳大檔案-斷點續傳的一中方式的記錄斷點
- 基於tcp的http應用,斷點續傳,範圍請求TCPHTTP斷點
- Java斷點續傳(基於socket與RandomAccessFile的簡單實現)Java斷點randomMac
- C# HTTP實現斷點續傳客戶端和服務端C#HTTP斷點客戶端服務端
- CUDA教學(2):反向傳播反向傳播
- PyTorch儲存模型斷點以及載入斷點繼續訓練PyTorch模型斷點