摘要:作者鐘離,酷家樂客戶端負(fù)責(zé)人原文地址酷家樂客戶端下載地址文章背景在酷家樂客戶端在改版成功后,我們積累了許多的寶貴的經(jīng)驗(yàn)和最佳實(shí)踐。
背景作者:鐘離,酷家樂PC客戶端負(fù)責(zé)人
原文地址:https://webfe.kujiale.com/electron-ku-jia-le-ke-hu-duan-kai-fa-shi-jian-fen-xiang-jin-cheng-tong-xin/
酷家樂客戶端:下載地址 https://www.kujiale.com/activity/136
文章背景:在酷家樂客戶端在V12改版成功后,我們積累了許多的寶貴的經(jīng)驗(yàn)和最佳實(shí)踐。前端社區(qū)里關(guān)于Electron知識(shí)相對(duì)較少,因此希望將這些內(nèi)容以系列文章的形式分享出來。
系列文章:【Electron】酷家樂客戶端開發(fā)實(shí)踐分享 — 入坑篇
【Electron】酷家樂客戶端開發(fā)實(shí)踐分享 — 軟件自動(dòng)更新
【Electron】酷家樂客戶端開發(fā)實(shí)踐分享 — 瀏覽器啟動(dòng)客戶端
【Electron】酷家樂客戶端開發(fā)實(shí)踐分享 — 進(jìn)程通信
【Electron】酷家樂客戶端開發(fā)實(shí)踐分享 — 下載管理器
不定期更新...
打開酷家樂客戶端,可以在左下角的更多菜單中找到下載管理這個(gè)功能,今天我們就來看看在Electron中如何實(shí)現(xiàn)一個(gè)下載管理器。
如何觸發(fā)下載行為由于Electron渲染層是基于chromium的,觸發(fā)下載的邏輯和chromium是一致的,頁面中的a標(biāo)簽或者js跳轉(zhuǎn)等等行為都可能觸發(fā)下載,具體視訪問的資源而定。什么樣的資源會(huì)觸發(fā)瀏覽器的下載行為呢?
response header中的Content-Disposition為attachment。參考MDN Content-Disposition
response header中的Content-Type,是瀏覽器無法直接打開的文件類型,例如application/octet-stream,此時(shí)取決于瀏覽器的具體實(shí)現(xiàn)了。例子: IE無法打開pdf文件,chrome可以直接打開pdf文件,因此pdf類型的url在chrome上可以直接打開,而在IE下會(huì)觸發(fā)下載行為。
在Electron中還有一種方法可以觸發(fā)下載: webContents.download。相當(dāng)于直接調(diào)用chromium底層的下載邏輯,忽略headers中的那些判斷,直接下載。
上述兩種下載行為,都會(huì)觸發(fā)session的will-download事件,在這里可以獲取到關(guān)鍵的downloadItem對(duì)象
整體流程 設(shè)置文件路徑如果不做任何處理的話,觸發(fā)下載行為時(shí)Electron會(huì)彈出一個(gè)系統(tǒng)dialog,讓用戶來選擇文件存放的目錄。這個(gè)體驗(yàn)并不好,因此我們首先需要把這個(gè)系統(tǒng)dialog去掉。使用downloadItem.savePath即可。
// Set the save path, making Electron not to prompt a save dialog. downloadItem.setSavePath("/tmp/save.pdf");
為文件設(shè)置默認(rèn)下載路徑,就需要考慮文件名重復(fù)的情況,一般來說會(huì)使用文件名自增的邏輯,例如:test.jpg、test.jpg(1)這種格式。文件默認(rèn)存放目錄,也是一個(gè)問題,我們統(tǒng)一使用app.getPath("downloads")作為文件下載目錄。為了用戶體驗(yàn),后續(xù)提供修改文件下載目錄功能即可。
// in main.js 主進(jìn)程中 const { session } = require("electron"); session.defaultSession.on("will-download", async (event, item) => { const fileName = item.getFilename(); const url = item.getURL(); const startTime = item.getStartTime(); const initialState = item.getState(); const downloadPath = app.getPath("downloads"); let fileNum = 0; let savePath = path.join(downloadPath, fileName); // savePath基礎(chǔ)信息 const ext = path.extname(savePath); const name = path.basename(savePath, ext); const dir = path.dirname(savePath); // 文件名自增邏輯 while (fs.pathExistsSync(savePath)) { fileNum += 1; savePath = path.format({ dir, ext, name: `${name}(${fileNum})`, }); } // 設(shè)置下載目錄,阻止系統(tǒng)dialog的出現(xiàn) item.setSavePath(savePath); // 通知渲染進(jìn)程,有一個(gè)新的下載任務(wù) win.webContents.send("new-download-item", { savePath, url, startTime, state: initialState, paused: item.isPaused(), totalBytes: item.getTotalBytes(), receivedBytes: item.getReceivedBytes(), }); // 下載任務(wù)更新 item.on("updated", (e, state) => { // eslint-disable-line win.webContents.send("download-item-updated", { startTime, state, totalBytes: item.getTotalBytes(), receivedBytes: item.getReceivedBytes(), paused: item.isPaused(), }); }); // 下載任務(wù)完成 item.on("done", (e, state) => { // eslint-disable-line win.webContents.send("download-item-done", { startTime, state, }); }); });
現(xiàn)在觸發(fā)下載行為,文件就已經(jīng)會(huì)下載到Downloads目錄了,文件名帶有自增邏輯。同時(shí),對(duì)下載窗口發(fā)送了關(guān)鍵事件,下載窗口可以根據(jù)這些事件和數(shù)據(jù),創(chuàng)建、更新下載任務(wù)。
上述步驟在渲染進(jìn)程使用remote實(shí)現(xiàn)會(huì)有問題,無法獲取到實(shí)時(shí)的下載數(shù)據(jù)。因此建議在主進(jìn)程實(shí)現(xiàn)。下載記錄
下載功能需要緩存下載歷史在本地,下載歷史的數(shù)據(jù)比較多,因此我們使用nedb作為本地?cái)?shù)據(jù)庫。
// 初始化 nedb 數(shù)據(jù)庫 const db = nedbStore({ filename, autoload: true }); ipcRenderer.on("new-download-item", (e, item) => { // 數(shù)據(jù)庫新增一條新紀(jì)錄 db.insert(item); // UI中新增一條下載任務(wù) this.addItem(item); }) // 更新下載窗口的任務(wù)進(jìn)度 ipcRenderer.on("download-item-updated", (e, item) => { this.updateItem(item) }) // 下載結(jié)束,更新數(shù)據(jù) ipcRenderer.on("download-item-done", (e, item) => { // 更新數(shù)據(jù)庫 db.update(item); // 更新UI中下載任務(wù)狀態(tài) this.updateItem(item); });
此時(shí)本地?cái)?shù)據(jù)庫中的數(shù)據(jù),是這樣的:
{"paused":false,"receivedBytes":0,"savePath":"/Users/ww/Downloads/酷家樂裝修網(wǎng)-保利金色佳苑-戶型圖.jpg","startTime":1560415098.731598,"state":"completed","totalBytes":236568,"url":"https://qhtbdoss.kujiale.com/fpimgnew/prod/3FO4EGX11S9S/op/LUBAVDQKN4BE6AABAAAAACY8.jpg?kpm=9V8.32a74ad82d44e7d0.3dba44f.1560415094020","_id":"6AorFZvpI0N8Yzw9"} {"paused":false,"receivedBytes":0,"savePath":"/Users/ww/Downloads/Kujiale-12.0.2-stable(1).dmg","startTime":1560415129.488072,"state":"progressing","totalBytes":80762523,"url":"https://qhstaticssl.kujiale.com/download/kjl-software12/Kujiale-12.0.2-stable.dmg?timestamp=1560415129351","_id":"YAeWIy2xoeWTw0Ht"} {"paused":false,"receivedBytes":0,"savePath":"/Users/ww/Downloads/酷家樂裝修網(wǎng)-保利金色佳苑-戶型圖(1).jpg","startTime":1560418413.240669,"state":"progressing","totalBytes":236568,"url":"https://qhtbdoss.kujiale.com/fpimgnew/prod/3FO4EGX11S9S/op/LUBBLFYKN4BE6AABAAAAADY8.jpg?kpm=9V8.32a74ad82d44e7d0.3dba44f.1560418409875","_id":"obFLotKillhzTw09"} {"paused":false,"receivedBytes":0,"savePath":"/Users/ww/Downloads/酷家樂裝修網(wǎng)-保利金色佳苑-戶型圖(1).jpg","startTime":1560418413.240669,"state":"completed","totalBytes":236568,"url":"https://qhtbdoss.kujiale.com/fpimgnew/prod/3FO4EGX11S9S/op/LUBBLFYKN4BE6AABAAAAADY8.jpg?kpm=9V8.32a74ad82d44e7d0.3dba44f.1560418409875","_id":"obFLotKillhzTw09"}
在渲染進(jìn)程初始化的時(shí)候,需要讀取下載記錄,數(shù)據(jù)按下載時(shí)間倒序。讀取數(shù)量需要做一下限制,否則會(huì)影響性能,暫時(shí)限制50條。
// 渲染進(jìn)程中 const db = nedbStore({ filename, autoload: true }); // 讀取歷史數(shù)據(jù) const downloadHistory = await db.cfind({}).sort({ startTime: -1, }).limit(50).exec() .catch(err => logger.error(err)); if (downloadHistory) { this.setList(downloadHistory.map((d) => { const item = d; // 歷史記錄中,只有需要未完成和完成兩個(gè)狀態(tài) if (item.state !== "completed") { item.state = "cancelled"; } return item; })); }自定義下載目錄
默認(rèn)下載目錄在Electron默認(rèn)為本機(jī)上的Downloads目錄,提供用戶設(shè)置下載目錄的功能,就需要在本地緩存用戶自定義的下載目錄。這種基礎(chǔ)配置我們使用electron-store來實(shí)現(xiàn)
// in config.json { "downloadsPath": "/Users/ww/Downloads/歸檔" }
在窗口初始化的時(shí)候,檢查緩存中是否有自定義下載目錄,如果有則更改app的默認(rèn)下載目錄
componentDidMount() { const downloadsPath = store.get("downloadsPath"); if (downloadsPath) { app.setPath("downloads", downloadsPath); // app.getPath("downloads"); -> /Users/ww/Downloads/歸檔 } }
用戶點(diǎn)擊更換下載目錄,此時(shí)需要以下步驟:
彈出文件目錄選擇dialog,使用dialog.showOpenDialog實(shí)現(xiàn)
更新本地緩存中的自定義下載目錄
修改當(dāng)前app的默認(rèn)下載目錄
更新下載窗口中的下載目錄文案
// 用戶點(diǎn)擊更改下載目錄的回調(diào) changeDoiwnloadHandler = () => { const paths = dialog.showOpenDialog({ title: "選擇文件存放目錄", properties: ["openDirectory"], }); if (paths && paths.length) { // 先更新一下本地緩存 store.set("downloadsPath", paths[0]); // 更新當(dāng)前的下載目錄 app.setPath("downloads", paths[0]); // 更新下載目錄文案 this.updateDownloadsPath(); } }計(jì)算下載進(jìn)度
拿到downloadItem之后,可以獲取到已下載的字節(jié)數(shù)和文件的總字節(jié)數(shù),以此來計(jì)算下載進(jìn)度。
const percent = item.getReceivedBytes() / item.getTotalBytes();操作文件
在下載管理窗口中,雙擊下載任務(wù)可以打開該文件,點(diǎn)擊查看按鈕可以打開文件所在目錄。我們統(tǒng)一使用Electron的shell模塊來實(shí)現(xiàn)。
openFile = (path) => { if (!fs.pathExistsSync) return; // 文件不存在的情況 shell.openItem(path); // 打開文件 } openFileFolder = async (path) => { if (!fs.pathExistsSync(path)) { // 文件不存在 return; } shell.showItemInFolder(path); // 打開文件所在文件夾 }獲取文件關(guān)聯(lián)圖標(biāo)
仔細(xì)觀察下載管理窗口我們可以發(fā)現(xiàn),文件的圖標(biāo)都是從系統(tǒng)獲取的,和我們?cè)谖募芾砥髦锌吹降奈募D標(biāo)一致。
上圖中dmg、jpg文件都展示了系統(tǒng)關(guān)聯(lián)的文件圖標(biāo),用戶體驗(yàn)很好。我們可以使用getFileIcon來獲取系統(tǒng)圖標(biāo),以下是具體實(shí)現(xiàn)代碼。
const { app } = require("electron").remote; // 封裝一個(gè)函數(shù) const getFileIcon = (path) => { return new Promise((resolve) => { const defaultIcon = "some-default.jpg"; if (!path) return resolve(defaultIcon); return app.getFileIcon(path, (err, nativeImage) => { if (err) { return resolve(defaultIcon); } return resolve(nativeImage.toDataURL()); // 使用base64展示圖標(biāo) }); }); }; // 獲取圖標(biāo) const imgSrc = await getFileIcon("./test.jpg");最后
歡迎大家在評(píng)論區(qū)討論,技術(shù)交流 & 內(nèi)推 -> zhongli@qunhemail.com
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/104704.html
摘要:作者鐘離,酷家樂客戶端負(fù)責(zé)人原文地址酷家樂客戶端下載地址文章背景在酷家樂客戶端在改版成功后,我們積累了許多的寶貴的經(jīng)驗(yàn)和最佳實(shí)踐。 作者:鐘離,酷家樂PC客戶端負(fù)責(zé)人原文地址:https://webfe.kujiale.com/electron-ku-jia-le-ke-hu-duan-kai-fa-shi-jian-fen-xiang-jin-cheng-tong-xin/酷家樂客...
摘要:作者鐘離,酷家樂客戶端負(fù)責(zé)人原文地址酷家樂客戶端下載地址文章背景在酷家樂客戶端在改版成功后,我們積累了許多的寶貴的經(jīng)驗(yàn)和最佳實(shí)踐。用戶在電腦上安裝客戶端,實(shí)際上會(huì)將客戶端代碼文件持久儲(chǔ)存到本機(jī)。通常我們會(huì)在軟件啟動(dòng)時(shí)檢查更新。 作者:鐘離,酷家樂PC客戶端負(fù)責(zé)人原文地址:https://webfe.kujiale.com/electron-autoupdate/酷家樂客戶端:下載地址...
摘要:作者鐘離,酷家樂客戶端負(fù)責(zé)人原文地址酷家樂客戶端下載地址文章背景在酷家樂客戶端在改版成功后,我們積累了許多的寶貴的經(jīng)驗(yàn)和最佳實(shí)踐。用戶在電腦上安裝客戶端,實(shí)際上會(huì)將客戶端代碼文件持久儲(chǔ)存到本機(jī)。通常我們會(huì)在軟件啟動(dòng)時(shí)檢查更新。 作者:鐘離,酷家樂PC客戶端負(fù)責(zé)人原文地址:https://webfe.kujiale.com/electron-autoupdate/酷家樂客戶端:下載地址...
摘要:作者鐘離,酷家樂客戶端負(fù)責(zé)人原文地址酷家樂客戶端下載地址文章背景在酷家樂客戶端在改版成功后,我們積累了許多的寶貴的經(jīng)驗(yàn)和最佳實(shí)踐。鐘離可以注冊(cè)多個(gè)協(xié)議接收參數(shù)協(xié)議注冊(cè)完畢之后,我們已經(jīng)可以在瀏覽器中,通過訪問自定義協(xié)議來啟動(dòng)客戶端了。 作者:鐘離,酷家樂PC客戶端負(fù)責(zé)人原文地址:https://webfe.kujiale.com/browser-to-client/酷家樂客戶端:下載...
閱讀 3801·2021-11-24 09:39
閱讀 1810·2021-11-02 14:41
閱讀 814·2019-08-30 15:53
閱讀 3480·2019-08-29 12:43
閱讀 1189·2019-08-29 12:31
閱讀 3086·2019-08-26 13:50
閱讀 795·2019-08-26 13:45
閱讀 986·2019-08-26 10:56