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

React前端无法上传文件至Express后端,日志解析器故障排查

问题分析与解决方案

你的问题核心在于文件上传的前后端处理逻辑错误,以及对FormData对象的理解偏差,下面分前端和后端给出具体修复方案:

前端修复

  1. FormData打印问题:直接console.log(formData)无法看到内部内容,因为FormData是不可枚举的特殊对象,需要用formData.get('logFile')或遍历entries查看:
// 替换原console.log(formData)
console.log('上传的文件:', formData.get('logFile'));
// 或者遍历所有项
for (let [key, value] of formData.entries()) {
  console.log(key, value);
}
  1. Axios请求头错误:手动设置"Content-Type": "multipart/form-data"会丢失自动生成的boundary分隔符,导致后端无法解析。axios会自动识别FormData并添加正确的头,所以删除该配置。

修改后的完整前端代码:

import React, { useState } from "react";
import axios from "axios";
import "./App.css";

const App = () => {
  const [logFile, setLogFile] = useState<File | null>(null);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setLogFile(e.target.files![0]);
  };

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (!logFile) return; // 增加文件非空校验

    try {
      const formData = new FormData();
      formData.append("logFile", logFile);
      // 正确打印FormData内容
      console.log('上传文件:', formData.get('logFile'));
      for (let [key, value] of formData.entries()) {
        console.log(key, value);
      }
      const response = await axios.post(
        "http://localhost:3001/logs",
        formData
      );
      console.log('解析结果:', response.data);
    } catch (error) {
      console.error('上传失败:', error);
    }
  };

  return (
    <>
      <div className="App">
        <h1>日志文件上传</h1>
        <form onSubmit={handleSubmit}>
          <div>
            <input type="file" onChange={handleChange} accept=".log" />
          </div>
          <button type="submit" disabled={!logFile}>提交</button>
        </form>
      </div>
    </>
  );
};

export default App;

后端修复

  1. 添加文件上传中间件:Express默认不处理multipart/form-data,需要安装multer来处理文件上传:
npm install multer
  1. 配置multer并修改接口逻辑:
    • 使用multer配置内存存储(也可配置磁盘存储)
    • 读取上传的文件内容,按行解析日志
    • 过滤error和warn级别的日志,同时处理解析异常

修改后的完整后端代码:

const express = require('express');
const app = express();
const port = 3001;
const winston = require('winston');
const cors = require("cors");
const multer = require('multer');
const fs = require('fs').promises; // 用异步文件读取

app.use(cors())

// 配置multer,使用内存存储
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });

const logger = winston.createLogger({
  transports: [
    new winston.transports.File({
      level: 'error',
      filename: 'error.log',
      handleExceptions: true,
      json: true,
      maxsize: 5242880,
      maxFiles: 5,
      colorize: false
    }),
    new winston.transports.File({
      level: 'warn',
      filename: 'warning.log',
      handleExceptions: true,
      json: true,
      maxsize: 5242880,
      maxFiles: 5,
      colorize: false
    })
  ],
  exitOnError: false
});

// 移除express.json(),因为接口处理的是文件上传而非JSON请求

app.get("/", (req, res) => {
  res.json({ "message": "日志解析服务运行中" })
})

// 使用upload.single('logFile')处理单文件上传,对应前端formData的key
app.post('/logs', upload.single('logFile'), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ error: '未上传文件' });
    }

    // 将二进制文件内容转成字符串,按行拆分日志
    const logContent = req.file.buffer.toString('utf8');
    const logLines = logContent.split('\n').filter(line => line.trim() !== '');

    const errorLogs = [];
    const warnLogs = [];

    // 解析每行JSON格式日志,兼容解析失败的情况
    for (const line of logLines) {
      try {
        const log = JSON.parse(line);
        if (log.level === 'error') {
          logger.error(log);
          errorLogs.push(log);
        } else if (log.level === 'warn') {
          logger.warn(log);
          warnLogs.push(log);
        }
      } catch (parseErr) {
        console.error('解析日志行失败:', parseErr, line);
      }
    }

    res.json({ errorLogs, warnLogs, totalLines: logLines.length });
  } catch (error) {
    console.error('处理日志失败:', error);
    res.status(500).json({ error: '服务器内部错误' });
  }
});

app.listen(port, () => {
  console.log(`服务运行在 http://localhost:${port}`);
});

关键说明

  • 前端:必须删除手动设置的multipart/form-data头,axios会自动处理boundary;增加文件非空校验,避免提交空表单。
  • 后端:通过multer获取上传的文件,读取buffer转成文本;解析日志时要处理可能的JSON解析错误,避免整个接口崩溃;不再需要express.json()中间件,因为接口处理的是文件上传请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 00:25:26