英文:
Electron - how to fetch a file from a local path and create a blob file
问题
I have this code in my background.js file of an electron app.
ipcMain.on('init', (event, ...args) => {
fileSelector = dialog.showOpenDialog({
title: 'Select template',
buttonLabel: 'Upload file',
filters: [
{
name: 'MS Word document', extensions: ['docx']
}
]
}).then((filePath) => {
//fs.readFile(filePath.filePaths[0], (err, data) => {
event.sender.send('documentPath', filePath.filePaths[0])
//})
}).catch((error) => console.warn(error))
})
Basically, I'm opening a native file selector, and I will send the path to the Vue front end of the app.
ipcRenderer.send('init')
ipcRenderer.receive('documentPath', (filePath) => {
// this.store.docTemplate = new Blob(data, { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }).arrayBuffer()
fs.readFile(filePath, (error, data) => {
this.store.docTemplate = data
})
console.log(this.store.docTemplate)
})
I'm calling the listener on the ipcRenderer
in the mounted hook of Vue.js. What I need to do is to read the file from the received file path and obtain a blob as would normally occur if the desired file is obtained using an <input type="file">
. I need to pass the blob to this library, so is there a way to accomplish this? I've tried enabling node integration, but this causes problems with IPC processes.
Thank you for the help.
英文:
I have this code in my background.js file of an electron app.
ipcMain.on('init', (event, ...args) => {
fileSelector = dialog.showOpenDialog({
title: 'Select template',
buttonLabel: 'Upload file',
filters: [
{
name: 'MS Word document', extensions: ['docx']
}
]
}).then( (filePath) => {
//fs.readFile(filePath.filePaths[0], (err, data) => {
event.sender.send('documentPath', filePath.filePaths[0])
//})
}).catch( (error) => console.warn(error) )
})
Basically I'm opening a native file selector and I will send the path on the vue front end of the app
ipcRenderer.send('init')
ipcRenderer.receive('documentPath', (filePath) => {
//this.store.docTemplate = new Blob(data, { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }).arrayBuffer()
fs.readFile(filePath, (error, data) => {
this.store.docTemplate = data
})
console.log(this.store.docTemplate)
})
I'm calling the listener on the ipcRenderer
in the mounted hook of vuejs. What I need to do is to read the file from the received file path and obtain a blob like will occur normally if the desired file is obtained using an <input type="file">
. I need to pass the blob to this library, so is there a way to accomplish this? I've tried to enable the node integration, but this will give me problems with ipc processes.
Thank you for the help.
答案1
得分: 1
以下是翻译好的部分:
经过评论讨论和本地测试,似乎 easy-template-x
不支持浏览器中的 ArrayBuffer
对象,尽管 README 中有提到支持。
但这不是问题,因为您可以完全在主线程中处理模板。这是应该完成这种工作的地方,这样您的渲染器可以执行更多的渲染工作。尽量充分利用第二个进程。JavaScript 只有一个线程,所以有两个进程是真正的奢侈!
我修改了您的示例代码,以展示如何完成这项工作,并使用了 async/await
使一切更加可读。您可能需要在周围添加一些更多的检查,但对我来说,使用 easy-template-x@3.1.0
完全没有问题。
我还将您的 send/on/receive
结构更改为更简单的结构,使用 ipcRenderer.invoke
配合 ipcMain.handle
。优点是主进程可以返回对调用的响应,这允许某种形式的双向通信。有关更多信息,请参阅Electron 文档的此页
main.js
const { readFile, writeFile } = require("fs/promises");
const { TemplateHandler } = require("easy-template-x");
ipcMain.handle("processTemplate", async (event, args) => {
const loadDialog = await dialog.showOpenDialog({
title: "Select template",
buttonLabel: "Upload file",
filters: [
{
name: "MS Word document",
extensions: ["docx"],
},
],
});
const selected = loadDialog.filePaths[0];
if (selected) {
const file = await readFile(selected);
const handler = new TemplateHandler();
const doc = await handler.process(file, params);
const saveDialog = await dialog.showSaveDialog({
title: "Save document",
buttonLabel: "Save document",
filters: [
{
name: "MS Word document",
extensions: ["docx"],
},
],
});
if (saveDialog.filePath) {
await writeFile(saveDialog.filePath, doc);
}
}
});
preload.js
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("templates", {
processTemplate: (params) => ipcRenderer.invoke("processTemplate", params),
});
renderer.js
async function process() {
await window.templates.processTemplate(templateParams);
}
如果对该代码有任何不清楚之处或有更多问题,请随时提问!
英文:
After discussion in the comments and some local testing, it has become apparent that easy-template-x
does not seem to support ArrayBuffer
objects in the browser, despite the README saying it does.
However, that is not a problem because you can completely handle the template in your main thread. That's where this kind of stuff should be done anyways so that your renderers can do more renderer stuff. It's best to take advantage of that second process as much as possible. JS has only a single thread, so having two processes is a real luxury!
I've modified your example code to show how that would be done and used async/await
to make everything more readable. You might need to add some more checks around that, but it worked just fine for me using `easy-template-x@3.1.0
I've also changed your send/on/receive
structure to a simpler one that uses ipcRenderer.invoke
paired with ipcMain.handle
The advantage is that the main process can return a response to the call, which allows some form of bidirectional communication. For more info, see this page of the Electron docs
main.js
const { readFile, writeFile } = require("fs/promises");
const { TemplateHandler } = require("easy-template-x");
ipcMain.handle("processTemplate", async (event, args) => {
const loadDialog = await dialog.showOpenDialog({
title: "Select template",
buttonLabel: "Upload file",
filters: [
{
name: "MS Word document",
extensions: ["docx"],
},
],
});
const selected = loadDialog.filePaths[0];
if (selected) {
const file = await readFile(selected);
const handler = new TemplateHandler();
const doc = await handler.process(file, params);
const saveDialog = await dialog.showSaveDialog({
title: "Save document",
buttonLabel: "Save document",
filters: [
{
name: "MS Word document",
extensions: ["docx"],
},
],
});
if (saveDialog.filePath) {
await writeFile(saveDialog.filePath, doc);
}
}
});
preload.js
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("templates", {
processTemplate: (params) => ipcRenderer.invoke("processTemplate", params),
});
renderer.js
async function process() {
await window.templates.processTemplate(templateParams);
}
If anything about that code is unclear or you have more questions, feel free to ask!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论