我正在尝试升级我的 Electron 应用程序,但没有节点集成和上下文隔离。我正在将文件保存代码从渲染器移至主进程。但是,我无法将 blob 传输到主进程。
错误信息是:
Uncaught (in promise) Error: An object could not be cloned.
main.js
const {app, BrowserWindow, dialog, ipcMain } = require('electron');
const path = require("path");
const fs = require("fs");
if (require('electron-squirrel-startup')) return app.quit();
try {require('electron-reloader')(module);} catch (_) {}
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
async function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 1024,
height: 768,
webPreferences: {
preload: path.join(__dirname, "preload.js") // use a preload script
},
autoHideMenuBar: true,
icon: __dirname + '/icons/icon.icns'
});
// and load the index.html of the app.
win.loadFile('index.html')
// Open the DevTools.
win.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
createWindow();
ipcMain.handle('dialog', (event, method, params) => {
return dialog[method](params);
});
ipcMain.handle('getPath', () => {
app.getPath('documents');
});
ipcMain.handle('writeFile', (event, filepath, blob) => {
var message = {};
fs.writeFile(filepath, blob, err => {
if (err) {
message.text = err;
message.title = 'Error Saving Table';
} else {
message.text = filepath;
message.title = 'Table saved to';
}
});
return message;
});
});
预加载.js
const {
ipcRenderer,
contextBridge
} = require("electron");
contextBridge.exposeInMainWorld('electron', {
openDialog: (method, params) => ipcRenderer.invoke('dialog', method, params),
getPath: () => ipcRenderer.invoke('getPath'),
writeFile: (filepath, blob) => ipcRenderer.invoke('writeFile', filepath, blob)
});
渲染器.js
function exportToExcelFile(tableID, anchorID, exportFilename) {
let format = 'xlsx';
let anchor = $$('#' + anchorID + '-' + format);
ExcellentExport.convert({
anchor: anchor[0],
filename: exportFilename,
format: format
}, [{
name: 'Sheet1',
from: {
table: $$('#' + tableID)[0]
}
}]);
// Get path, open save dialog and save file
window.electron.getPath()
.then((appDataPath) => {
const dialogConfig = {
filters: [{
name: 'Excel',
extensions: ['xlsx']
}],
defaultPath: appDataPath + '/' + exportFilename
};
electron.openDialog('showSaveDialogSync', dialogConfig).then(
filepath => {
if (filepath !== undefined) {
console.log(anchor.attr('href'));
fetch(anchor.attr('href'))
.then(res => res.blob())
.then(blob => {electron.writeFile(filepath, blob);
})
.then(message => {app.dialog.alert(message.text, message.title);
})
}
}
);
})
.catch(result => console.log(result));
}
您无法通过 IPC 将 Blob 从渲染器发送到主程序,在此之前您必须将其转换为 ArrayBuffer。
当你在 main 上收到 ArrayBuffer 后,为了能够将其写入文件系统,你必须将其转换为 Node.js 的全局 Buffer。
// renderer
const arrayBuffer = await blob.arrayBuffer()
ipcRenderer.send('save-blob', arrayBuffer)
// main
const buffer = Buffer.from(arrayBuffer)
fs.writeFile(filePath, buffer)
(您也可以使用 FileReader API 将 ArrayBuffer 直接从渲染器进程转换为 Buffer,然后将其发送到 main,这样就可以直接写入文件系统)