【Electron Playground 系列】文件下載篇

作者: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)一個簡單的文件下載管理器:

demo.gif

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. 下載流程

flow_chart.png

3. 功能設(shè)計

實現(xiàn)一個簡單的文件下載管理器功能包含:

  • 設(shè)置保存路徑
  • 暫停/恢復(fù)和取消
  • 下載進度
  • 下載速度
  • 下載完成
  • 打開文件和打開文件所在位置
  • 文件圖標
  • 下載記錄

3.1 設(shè)置保存路徑

如果沒有設(shè)置保存路徑蛾绎,electron 會自動彈出系統(tǒng)的保存對話框昆箕。不想使用系統(tǒng)的保存對話框鸦列,可以使用 setSavePath 方法,當有重名文件時鹏倘,會直接覆蓋下載薯嗤。

item.setSavePath(path)

為了更好的用戶體驗,可以讓用戶自己選擇保存位置操作纤泵。當點擊位置輸入框時骆姐,渲染進程通過 ipc 與主進程通信,打開系統(tǒng)文件選擇對話框捏题。


select_path.gif

主進程實現(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公荧、resumecancel 方法带射。當我們要刪除列表中正在下載的項,需要先調(diào)用 cancel 方法取消下載循狰。

3.3 下載進度

在 DownloadItem 中監(jiān)聽 updated 事件窟社,可以實時獲取到已下載的字節(jié)數(shù)據(jù),來計算下載進度和每秒下載的速度晤揣。

// 計算下載進度
const progress = item.getReceivedBytes() / item.getTotalBytes()
download_progress.png

在下載的時候桥爽,想在 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)程序塢)顯示效果:


windows_progress.png

mac_download_progress.png
// 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 一次报亩。

updated_event.png

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)注我們完疫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市亲轨,隨后出現(xiàn)的幾起案子趋惨,更是在濱河造成了極大的恐慌,老刑警劉巖惦蚊,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件器虾,死亡現(xiàn)場離奇詭異讯嫂,居然都是意外死亡,警方通過查閱死者的電腦和手機兆沙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門欧芽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人葛圃,你說我怎么就攤上這事千扔。” “怎么了库正?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵曲楚,是天一觀的道長。 經(jīng)常有香客問我褥符,道長龙誊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任喷楣,我火速辦了婚禮趟大,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘铣焊。我一直安慰自己逊朽,他們只是感情好,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布曲伊。 她就那樣靜靜地躺著叽讳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪熊昌。 梳的紋絲不亂的頭發(fā)上绽榛,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音婿屹,去河邊找鬼。 笑死推溃,一個胖子當著我的面吹牛昂利,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铁坎,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼蜂奸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了硬萍?” 一聲冷哼從身側(cè)響起扩所,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎朴乖,沒想到半個月后祖屏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體助赞,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年袁勺,在試婚紗的時候發(fā)現(xiàn)自己被綠了雹食。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡期丰,死狀恐怖群叶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钝荡,我是刑警寧澤街立,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站埠通,受9級特大地震影響赎离,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜植阴,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一蟹瘾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧掠手,春花似錦憾朴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至做祝,卻和暖如春砾省,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背混槐。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工编兄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人声登。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓狠鸳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親悯嗓。 傳聞我的和親對象是個殘疾皇子件舵,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容