最近用 Electron 做了個壁紙程序,需要斷點續下載,在這里記錄一下。
HTTP斷點下載相關的報文
- Accept-Ranges
告訴客戶端服務器是否支持斷點續傳,服務器返回 - Content-Range
在HTTP協議中,響應首部 Content-Range 顯示的是一個數據片段在整個文件中的位置。 - ETag
資源標識 非必須 服務器返回 - Last-Modified
資源最后一次更新的時間 非必須 服務器返回
//響應示例
accept-ranges: bytes
Content-Range: bytes 200-1000/67589 // 返回文件 200字節到1000字節 的數據,總文件大小67589字節
etag: "5f0dce96-48e"
last-modified: Tue, 14 Jul 2020 15:26:14 GMT
- Range
請求頭設置Range, 指定服務器返回指定區域內容,如果不設置Range會返回整個文件。服務器片段返回狀態碼是206,請求的范圍如果無效狀態碼會是416,全部返回狀態碼是200
//示例
Range: bytes=0-499 表示第 0-499 字節范圍的內容
Range: bytes=500-999 表示第 500-999 字節范圍的內容
Range: bytes=-500 表示最后 500 字節的內容
Range: bytes=500- 表示從第 500 字節開始到文件結束部分的內容
Range: bytes=0-0,-1 表示第一個和最后一個字節
Range: bytes=500-600,601-999 同時指定幾個范圍
Electron 斷點續下載方式
- 使用 Chromium 的下載功能,在主進程里監聽 will-download 事件去處理
- 使用 Electron 的net模塊或者 Node.js 的http/https模塊自己創建請求,記錄已下載位置
使用 Chromium 的下載
可以在渲染進程中和網頁一樣進行觸發下載(例如a標簽),也可以在主進程中使用 BrowserWindow.webContents 或 session 的 downloadURL觸發下載
//示例
//使用窗體 創建下載事件和監聽
let win = new BrowserWindow()
win.webContents.downloadURL(url)
win.webContents.session.on('will-download', (event, downloadItem, webContents) => {
event.preventDefault()//可以阻止下載
//downloadItem 下載項目的 EventEmitter
})
//或者 使用默認session對象。
session.defaultSession.downloadURL(url)
session.defaultSession.on('will-download', (event, downloadItem, webContents) => {
})
然后可以 will-download 事件中的 downloadItem 實例去存儲下載信息。等待程序再次啟動時通過 session.createInterruptedDownload 恢復上一次的下載
大致流程
//一個簡易示例
let cacheItem = {}
session.defaultSession.on('will-download', (e, item) => {
const url = item.getURL()
// 獲取文件的總大小
const totalBytes = item.getTotalBytes();
// 設置下載路徑
const filePath = path.join(app.getPath("downloads"), item.getFilename());
item.setSavePath(filePath);
// 緩存downitem 將這些信息保存下來,
cacheItem.path = item.getSavePath();//圖片地址
cacheItem.eTag = item.getETag();//資源標識
cacheItem.urlChain = item.getURLChain();//地址
cacheItem.length = totalBytes//資源大小
cacheItem.lastModified = item.getLastModifiedTime()//資源最后一次更新的時間
cacheItem.startTime = item.getStartTime();
let lastBytes = 0;
// 監聽下載過程,計算並設置進度條進度
item.on('updated', (event, state) => {
if (state === 'interrupted') {
console.log('下載已經中斷,可以恢復')
} else if (state === 'progressing') {
if (item.isPaused()) {
console.log('暫停下載')
} else {
let offset = item.getReceivedBytes();
cacheItem.speedBytes = offset - lastBytes;//下載速度
cacheItem.offset = offset//已經下載
lastBytes = offset
console.log('下載中')
}
}
})
//
item.once('done', (event, state) => {
if (state === 'interrupted') {
console.log('下載已經中斷,無法恢復')
}
else if (state === 'cancelle') {
console.log('下載取消')
}
else {
console.log('下載完成')
}
})
//是否可恢復下載
if (item.canResume) {
item.resume()
}
})
//程序關閉時將 cacheItem 存儲下載
// ===> 程序 再次打開時
// 將上面存儲cacheItem信息讀取出來 恢復下載
session.defaultSession.createInterruptedDownload({
path,
urlChain,
offset, // 下載斷點開始位置
length,
lastModified, //
eTag, //
startTime
})
相關的文檔地址 BrowserWindow ,Session , webContents, DownloadItem
創建請求實現續下載
大致上和上面是差不多的,記錄已下載文件信息,再次請求時候設置從指定位置開始請求數據