You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 = '发生错误,请检查服务器是否正常运行。';
        }
    });
});
问题解决说明
  1. FormData解析问题:原代码中body-parser无法处理multipart/form-data格式的请求,添加multer中间件并使用upload.none()解析FormData,确保req.body能正确获取表单字段。
  2. Electron安全策略:在创建BrowserWindow时配置webPreferences,确保允许访问localhost服务器,避免跨域或协议限制。
  3. 二进制文件处理:前端原代码用response.text()处理docx二进制文件会导致乱码,改为response.blob()并创建下载链接,符合docx文件的处理逻辑。
  4. 清理冗余代码:移除主进程中不必要的readline模块调用,避免Electron应用运行时出现异常。

内容的提问来源于stack exchange,提问作者Mahmoud Othmane

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 00:40:22