Electron应用提交表单报ERR_FILE_NOT_FOUND,需与浏览器端表现一致
问题描述
我开发了一款基于docxtemplater的应用,在浏览器中可正常读取docx模板、接收HTML页面用户输入并生成目标文档。但迁移到Electron后,点击提交按钮出现错误:(node:2792) electron: Failed to load URL: file:///C:/process-document with error: ERR_FILE_NOT_FOUND。最初使用fetch('/process-document')无效,改为fetch('http://localhost:3000/process-document')仍报错,希望Electron应用能和浏览器端正常工作时表现一致。
主进程代码
const express = require('express'); const bodyParser = require('body-parser'); const PizZip = require("pizzip"); const Docxtemplater = require("docxtemplater"); const fs = require('fs'); const path = require('path'); const electron = require('electron'); const url = require('url'); const {app, BrowserWindow, Menu} = electron; app.on('ready', function() { // 创建窗口并配置web安全策略 mainWindow = new BrowserWindow({ webPreferences: { nodeIntegration: false, contextIsolation: true, webSecurity: true } }); // 加载本地HTML文件 mainWindow.loadURL(url.format({ pathname: path.join(__dirname, 'index.html'), protocol: 'file:', slashes: true })); // 关闭窗口时退出应用 mainWindow.on('closed', function() { app.quit(); }); // 构建应用菜单 const mainMenu = Menu.buildFromTemplate(mainMenuTemplate); Menu.setApplicationMenu(mainMenu); }); const mainMenuTemplate = [ { label: 'File', submenu:[ { label: 'Add Item', click(){ console.log("add an item") } }, { label:'Clear Item' }, { label: 'Quit', accelerator: process.platform == 'darwin' ? 'Command+Q' : 'Ctrl+Q', click(){ app.quit() } } ] } ]; const appyy = express(); const port = 3000; const multer = require('multer'); const upload = multer(); // 用于解析FormData appyy.use(express.static(path.join(__dirname, 'public'))); appyy.use(bodyParser.urlencoded({ extended: true })); appyy.use(bodyParser.json()); appyy.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); }); appyy.post('/process-document', upload.none(), (req, res) => { console.log('req.body: ', req.body); const { firstName, lastName, telephone, faxNum, address, dateOfBirth, birthPlace, idNum, idDate, daira, wilaya, commune, number} = req.body; const todayDate = getFormattedDate(); // 加载docx模板 const content = fs.readFileSync(path.resolve(__dirname, 'public', "FilahaNew.docx"), "binary"); const zip = new PizZip(content); const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true, }); // 渲染文档 doc.render({ firstName, lastName, telephone, faxNum, address, dateOfBirth, birthPlace, idNum, idDate, daira, wilaya, commune, number, todayDate, }); console.log({firstName, lastName, telephone, faxNum, address, dateOfBirth, birthPlace, idNum, idDate, daira, wilaya, commune, number}); const generatedDocBuffer = doc.getZip().generate({ type: "nodebuffer", compression: "DEFLATE", }); // 保存生成的文档到本地(可选) const outputPath = path.resolve(__dirname, 'public', `${firstName}${lastName}.docx`); fs.writeFileSync(outputPath, generatedDocBuffer); console.log(`Filled template saved as "${outputPath}"`); // 返回生成的文档给前端 res.setHeader('Content-Type', 'application/msword'); res.setHeader('Content-Disposition', 'attachment; filename=output.docx'); res.send(generatedDocBuffer); }); appyy.listen(port, () => { console.log(`Server is running on http://localhost:${port}`); }); function getFormattedDate() { const today = new Date(); const day = String(today.getDate()).padStart(2, '0'); const month = String(today.getMonth() + 1).padStart(2, '0'); const year = today.getFullYear(); return `${day}/${month}/${year}`; }
script.js代码
console.log('Script loaded'); document.addEventListener('DOMContentLoaded', function () { const form = document.getElementById('documentForm'); const output = document.getElementById('output'); form.addEventListener('submit', async function (event) { event.preventDefault(); console.log('Form submitted'); const formData = new FormData(form); try { const response = await fetch('http://localhost:3000/process-document', { method: 'POST', body: formData, }); if (response.ok) { console.log('response OK'); // 处理二进制docx文件 const blob = await response.blob(); const url = window.URL.createObjectURL(blob); // 创建下载链接 const downloadLink = document.createElement('a'); downloadLink.href = url; downloadLink.download = 'output.docx'; document.body.appendChild(downloadLink); downloadLink.click(); // 清理资源 window.URL.revokeObjectURL(url); document.body.removeChild(downloadLink); output.textContent = '文档生成成功,已自动下载'; } else { console.log('请求失败'); output.textContent = '生成文档时出错,请重试。'; } } catch (error) { console.error('Error:', error); output.textContent = '发生错误,请检查服务器是否正常运行。'; } }); });
问题解决说明
- FormData解析问题:原代码中
body-parser无法处理multipart/form-data格式的请求,添加multer中间件并使用upload.none()解析FormData,确保req.body能正确获取表单字段。 - Electron安全策略:在创建
BrowserWindow时配置webPreferences,确保允许访问localhost服务器,避免跨域或协议限制。 - 二进制文件处理:前端原代码用
response.text()处理docx二进制文件会导致乱码,改为response.blob()并创建下载链接,符合docx文件的处理逻辑。 - 清理冗余代码:移除主进程中不必要的
readline模块调用,避免Electron应用运行时出现异常。
内容的提问来源于stack exchange,提问作者Mahmoud Othmane
相关产品推荐
相关产品推荐

