Electron構建下載文件桌面應用

做這個真的要瘋掉,但是開始了就要做出來。
下面就說一下整個過程吧,因爲真的查閱了不少資料,發現似乎沒幾個桌面應用會選擇做下載文件的,所以需要一直查資料查資料,查到要瘋掉。
進入正題


初識electron

electron文檔:https://github.com/electron/electron
文檔裏面有中文的,所以還是蠻容易理解的。
文檔主要就是介紹electron項目的結構如下:
這裏寫圖片描述
其中package.json是用來描述一些配置信息以及一些快速啓動的指令等信息的
main.js則是用來整個項目的主線程,用於創建窗口和處理系統事件。
index.html就是佈局啦。

運行

package.json文件裏有這樣一句描述:
這裏寫圖片描述
這就是快速啓動指令,只需要npm start就可以啓動你的electron應用了。


創建渲染器進程

對於渲染器進程,大家給的理解是主線程是無法顯示的,需要通過BrowserWindow來創建新的窗口,每一個窗口維護一個渲染器進程。
我的文件目錄如下:
這裏寫圖片描述
app目錄下的js/index.js就可以看作一個渲染器進程。

主進程和渲染器進程的區別

這裏寫圖片描述


界面

界面可以通過html+css實現
我的界面如下:
這裏寫圖片描述


選擇文件存放文件夾

此處需要用到electron的一個模塊dialog,但是由於其爲主線程內可用的模塊,所以在渲染器進程使用的時候必須加上.remote

remote 模塊提供了一種在渲染進程(網頁)和主進程之間進行進程間通訊(IPC)的簡便途徑。

所以在渲染器進程處加上以下語句獲取dialog模塊

const {dialog} = require('electron').remote

選擇文件夾具體語句如下

button_choose.addEventListener("click", function(){
    dialog.showOpenDialog({
        //默認路徑
        defaultPath :'../Desktop',
        //選擇操作,此處是打開文件夾
        properties: [
            'openDirectory',
        ],
        //過濾條件
        filters: [
            { name: 'All', extensions: ['*'] },
        ]
    },function(res){
        //回調函數內容,此處是將路徑內容顯示在input框內
        downloadFolder.value = res[0];
    })
});

實現下載文件

這就是我最爆炸的部分。完成這個應用的一半時間都貢獻給了這部分。
先進行簡單的校驗

button_download.addEventListener("click", function(){
    var tips = document.getElementsByClassName("tips")[0];
    if(downloadFolder.value!=""
       &&downloadAddress.value!="") {
        //下載文件
    } else if(downloadAddress.value=="") {
        tips.innerText = "未填寫下載地址";
    } else {
        tips.innerText = "未選擇文件夾"
    }
})

下載文件我使用的是will-download,尋找一個合適的模塊實現真的很心累,這裏就不贅述了。
由於will-download是主進程的模塊調用的,所以此處就需要主進程和渲染進程的通信。

ipcMain和ipcRenderer

通信原理如下:

// 後臺進程
const {ipcMain} = require('electron')
ipcMain.on('create', (event, person) => {
  console.log('creating', person)    // 輸出:"creating harttle"
  event.sender.send('born', person)
});

// 渲染進程
const {ipcRenderer} = require('electron')
ipcRenderer.on('born', (event, person) => {
  console.log(person, 'born')       // 輸出 "harttle born"
});
ipcRenderer.send('create', 'harttle')

所以我的想法是,在主進程裏監聽一個download事件,在渲染進程裏當點擊下載時調用這個事件。

//主進程代碼
ipcMain.on('download', (evt, args) => {
    var arr = args.split("+");
    downloadpath = arr[0];
    folderpath = arr[1];
    evt.sender.send('tips',downloadpath);      
    mainWindow.webContents.downloadURL(downloadpath);
});
//渲染器進程代碼
ipcRenderer.send('download',downloadAddress.value+"+"+downloadFolder.value);

下面這句會觸發will-download事件

mainWindow.webContents.downloadURL(downloadpath);

下面來設置監聽will-download事件的回調函數

mainWindow.webContents.session.on('will-download', (event, item, webContents) => {
    //設置文件存放位置
    item.setSavePath(folderpath+`\\${item.getFilename()}`);
  item.on('updated', (event, state) => {
    if (state === 'interrupted') {
      console.log('Download is interrupted but can be resumed')
    } else if (state === 'progressing') {
      if (item.isPaused()) {
        console.log('Download is paused')
      } else {
        console.log(`Received bytes: ${item.getReceivedBytes()}`)
      }
    }
  })
  item.once('done', (event, state) => {
    if (state === 'completed') {
      console.log('Download successfully')
    } else {
      console.log(`Download failed: ${state}`)
    }
  })
})

ipc到ipcMain和ipcRender

其實我一開始使用的是ipc進行兩個進程之間的通信,但是不論我怎麼定義,ipc總是報錯,報其沒有send和on方法。
又瘋狂找資料,遇到這個問題的人還真不多,所以沒有找到解決方法。
但是後來發現,與主進程的通信可以使用ipcMain,所以就棄用ipc了。
找不到爲什麼錯真的很傷自尊啊,希望下次可以發現。

主進程更改後刷新頁面沒有改變

這個的解決方法是退出應用然後重新npm start一次。
這個很好理解吧,重新刷新頁面只是更新了渲染器進程而非主進程。

item.setSavePath()問題

這個方法接受一個參數即文件存放路徑。
但是!一定保證路徑裏包含了文件名!一定!
否則,就算路徑不正確它也不會報錯而是選擇存放在默認路徑下。

ipcMain和ipcRender的傳參問題

ipcMain/ipcRender.send(eventname, arg);

此處的arg一個參數而不是參數數組
所以由於要傳下載地址和文件存放地址,我選擇使用“+”把它們連接起來。


總結:

除了找出錯在哪裏其他地方還是蠻有趣的吧。
感覺electron的機制和redux還是蠻像的,其中有主進程負責處理系統調用,而渲染進程負責觸發事件。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章