作者:long.woo
文件下載是我們開發(fā)中比較常見的業(yè)務(wù)需求,比如:導(dǎo)出 excel。
web 應(yīng)用文件下載存在一些局限性,通常是讓后端將響應(yīng)的頭信息改成 Content-Disposition: attachment; filename=xxx.pdf
,觸發(fā)瀏覽器的下載行為。
在 electron 中的下載行為备禀,都會觸發(fā) session 的 will-download 事件。在該事件里面可以獲取到 downloadItem 對象奈揍,通過 downloadItem 對象實現(xiàn)一個簡單的文件下載管理器:
1. 如何觸發(fā)下載
由于 electron 是基于 chromium 實現(xiàn)的曲尸,通過調(diào)用 webContents 的 downloadURL 方法,相當于調(diào)用了 chromium 底層實現(xiàn)的下載男翰,會忽略響應(yīng)頭信息另患,觸發(fā) will-download 事件。
// 觸發(fā)下載
win.webContents.downloadURL(url)
// 監(jiān)聽 will-download
session.defaultSession.on('will-download', (event, item, webContents) => {})
2. 下載流程
3. 功能設(shè)計
實現(xiàn)一個簡單的文件下載管理器功能包含:
- 設(shè)置保存路徑
- 暫停/恢復(fù)和取消
- 下載進度
- 下載速度
- 下載完成
- 打開文件和打開文件所在位置
- 文件圖標
- 下載記錄
3.1 設(shè)置保存路徑
如果沒有設(shè)置保存路徑蛾绎,electron 會自動彈出系統(tǒng)的保存對話框昆箕。不想使用系統(tǒng)的保存對話框鸦列,可以使用 setSavePath 方法,當有重名文件時鹏倘,會直接覆蓋下載薯嗤。
item.setSavePath(path)
為了更好的用戶體驗,可以讓用戶自己選擇保存位置操作纤泵。當點擊位置輸入框時骆姐,渲染進程通過 ipc 與主進程通信,打開系統(tǒng)文件選擇對話框捏题。
主進程實現(xiàn)代碼:
/**
* 打開文件選擇框
* @param oldPath - 上一次打開的路徑
*/
const openFileDialog = async (oldPath: string = app.getPath('downloads')) => {
if (!win) return oldPath
const { canceled, filePaths } = await dialog.showOpenDialog(win, {
title: '選擇保存位置',
properties: ['openDirectory', 'createDirectory'],
defaultPath: oldPath,
})
return !canceled ? filePaths[0] : oldPath
}
ipcMain.handle('openFileDialog', (event, oldPath?: string) => openFileDialog(oldPath))
渲染進程代碼:
const path = await ipcRenderer.invoke('openFileDialog', 'PATH')
3.2 暫停/恢復(fù)和取消
拿到 downloadItem 后玻褪,暫停、恢復(fù)和取消分別調(diào)用 pause
公荧、resume
和 cancel
方法带射。當我們要刪除列表中正在下載的項,需要先調(diào)用 cancel 方法取消下載循狰。
3.3 下載進度
在 DownloadItem 中監(jiān)聽 updated 事件窟社,可以實時獲取到已下載的字節(jié)數(shù)據(jù),來計算下載進度和每秒下載的速度晤揣。
// 計算下載進度
const progress = item.getReceivedBytes() / item.getTotalBytes()
在下載的時候桥爽,想在 Mac 系統(tǒng)的程序塢和 Windows 系統(tǒng)的任務(wù)欄展示下載信息,比如:
- 下載數(shù):通過 app 的 badgeCount 屬性設(shè)置昧识,當為 0 時,不會顯示盗扒。也可以通過 dock 的 setBadge 方法設(shè)置跪楞,該方法支持的是字符串,如果不要顯示侣灶,需要設(shè)置為 ''甸祭。
- 下載進度:通過窗口的 setProgressBar 方法設(shè)置。
由于 Mac 和 Windows 系統(tǒng)差異褥影,下載數(shù)僅在 Mac 系統(tǒng)中生效池户。加上 process.platform === 'darwin' 條件,避免在非 Mac凡怎、Linux 系統(tǒng)下出現(xiàn)異常錯誤校焦。
下載進度(Windows 系統(tǒng)任務(wù)欄、Mac 系統(tǒng)程序塢)顯示效果:
// mac 程序塢顯示下載數(shù):
// 方式一
app.badgeCount = 1
// 方式二
app.dock.setBadge('1')
// mac 程序塢统倒、windows 任務(wù)欄顯示進度
win.setProgressBar(progress)
3.4 下載速度
由于 downloadItem 沒有直接為我們提供方法或?qū)傩垣@取下載速度寨典,需要自己實現(xiàn)。
思路:在 updated 事件里通過 getReceivedBytes 方法拿到本次下載的字節(jié)數(shù)據(jù)減去上一次下載的字節(jié)數(shù)據(jù)房匆。
// 記錄上一次下載的字節(jié)數(shù)據(jù)
let prevReceivedBytes = 0
item.on('updated', (e, state) => {
const receivedBytes = item.getReceivedBytes()
// 計算每秒下載的速度
downloadItem.speed = receivedBytes - prevReceivedBytes
prevReceivedBytes = receivedBytes
})
需要注意的是耸成,updated 事件執(zhí)行的時間約 500ms 一次报亩。
3.5 下載完成
當一個文件下載完成、中斷或者被取消井氢,需要通知渲染進程修改狀態(tài)弦追,通過監(jiān)聽 downloadItem 的 done 事件。
item.on('done', (e, state) => {
downloadItem.state = state
downloadItem.receivedBytes = item.getReceivedBytes()
downloadItem.lastModifiedTime = item.getLastModifiedTime()
// 通知渲染進程花竞,更新下載狀態(tài)
webContents.send('downloadItemDone', downloadItem)
})
3.6 打開文件和打開文件所在位置
使用 electron 的 shell 模塊來實現(xiàn)打開文件(openPath)和打開文件所在位置(showItemInFolder)骗卜。
由于 openPath 方法支持返回值
Promise<string>
,當不支持打開的文件左胞,系統(tǒng)會有相應(yīng)的提示寇仓,而 showItemInFolder 方法返回值是void
。如果需要更好的用戶體驗烤宙,可使用 nodejs 的 fs 模塊遍烦,先檢查文件是否存在。
import fs from 'fs'
// 打開文件
const openFile = (path: string): boolean => {
if (!fs.existsSync(path)) return false
shell.openPath(path)
return true
}
// 打開文件所在位置
const openFileInFolder = (path: string): boolean => {
if (!fs.existsSync(path)) return false
shell.showItemInFolder(path)
return true
}
3.7 文件圖標
很方便的是使用 app 模塊的 getFileIcon 方法來獲取系統(tǒng)關(guān)聯(lián)的文件圖標躺枕,返回的是 Promise<NativeImage>
類型服猪,我們可以用 toDataURL 方法轉(zhuǎn)換成 base64,不需要我們?nèi)ヌ幚聿煌募愋惋@示不同的圖標拐云。
const getFileIcon = async (path: string) => {
const iconDefault = './icon_default.png'
if (!path) Promise.resolve(iconDefault)
const icon = await app.getFileIcon(path, {
size: 'normal'
})
return icon.toDataURL()
}
3.8 下載記錄
隨著下載的歷史數(shù)據(jù)越來越多罢猪,使用 electron-store 將下載記錄保存在本地。
對 Electron 感興趣叉瘩?請關(guān)注我們的開源項目 Electron Playground膳帕,帶你極速上手 Electron。
我們每周五會精選一些有意思的文章和消息和大家分享薇缅,來掘金關(guān)注我們的 曉前端周刊危彩。
我們是好未來 · 曉黑板前端技術(shù)團隊。
我們會經(jīng)常與大家分享最新最酷的行業(yè)技術(shù)知識泳桦。
歡迎來 知乎汤徽、掘金、Segmentfault灸撰、CSDN谒府、簡書、開源中國浮毯、博客園 關(guān)注我們完疫。