文件上传至API排序时触发500内部服务器错误,求排查
问题分析与修复方案
问题描述
尝试将文件上传至运行在3000端口的API,对文件中的数字排序后生成可下载的结果文件,但上传后触发计算时出现Internal Server Error(500内部服务器错误)。React应用运行在3001端口,以下是两端代码及问题排查:
原API代码(app.js)
const express = require('express'); const multer = require('multer'); const bodyParser = require('body-parser'); const fs = require('fs'); const app = express(); const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); app.use(bodyParser.text({ type: 'text/plain' })); app.post('/sort', upload.single('inputFile'), (req, res) => { console.log(req.file) const input = req.file.buffer.toString().split('\n').map(Number); const result = input.sort((a, b) => b - a); const resultText = result.join('\n'); fs.writeFile('result.txt', resultText, (err) => { if(err) throw err; res.send('File succesfully sorted!'); }); res.set('Content-Type', 'text/plain'); res.send(resultText); }); app.listen(3000, () => { console.log('API is listening on port 3000'); });
原React代码(App.js片段)
const [inputFile, setInputFile] = useState(null); const [result, setResult] = useState(null); const [processingTime, setProcessingTime] = useState(null); const handleFileUpload = (event) => { setInputFile(event.target.files[0]); }; const startCalculation = async (event) => { event.preventDefault(); const startTime = performance.now(); const formData = new FormData(); formData.append('inputFile', inputFile); console.log(inputFile) const response = await fetch("http://localhost:3000/sort", { method: 'POST', body: formData, mode: 'no-cors', }); const data = await response.text(); console.log(data); setResult(data); setProcessingTime(performance.now() - startTime); }; const handleDownload = (event) => { event.preventDefault(); const file = new Blob([result], { type: 'text/plain' }); const fileURL = URL.createObjectURL(file); const link = document.createElement('a'); link.href = fileURL; link.download = 'result.txt'; link.click(); };
核心问题与修复步骤
1. API端的致命错误
- 重复发送响应:代码中先后调用两次
res.send,HTTP响应仅能发送一次,重复调用直接触发服务器错误。 - bodyParser与multer冲突:
bodyParser.text会尝试解析所有请求为文本,干扰multer对multipart/form-data格式文件的解析,导致req.file为undefined,后续访问buffer时报错。 - 错误处理不当:
fs.writeFile回调中直接throw err会终止服务器进程,应返回错误响应给客户端。 - 未处理无效数据:文件中的空行或非数字内容会被转为
NaN,导致排序逻辑异常。 - 跨域未配置:React与API端口不同,未配置CORS会引发跨域限制。
2. React端的错误
mode: 'no-cors'限制响应读取:该模式下浏览器会屏蔽响应内容,response.text()会失败,无法感知服务器错误。- 未检查文件是否存在:用户未选文件就提交时,
formData无文件字段,API端req.file为undefined触发报错。 - 未处理响应错误状态:未判断
response.ok,服务器返回500错误时仍尝试读取内容,导致前端报错。
修复后的代码
修复后的API代码(app.js)
先安装cors依赖:
npm install cors
const express = require('express'); const multer = require('multer'); const fs = require('fs'); const cors = require('cors'); const app = express(); // 配置CORS允许3001端口跨域访问 app.use(cors({ origin: 'http://localhost:3001' })); const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); // 移除干扰multer的bodyParser.text配置 // app.use(bodyParser.text({ type: 'text/plain' })); app.post('/sort', upload.single('inputFile'), (req, res) => { // 检查是否接收到文件 if (!req.file) { return res.status(400).send('请上传文件'); } try { // 解析文件并过滤无效内容 const input = req.file.buffer.toString() .split('\n') .map(str => str.trim()) .filter(str => str) .map(Number) .filter(num => !isNaN(num)); // 降序排序 const result = input.sort((a, b) => b - a); const resultText = result.join('\n'); // 保存结果到服务器(可选,不需要可删除这段) fs.writeFile('result.txt', resultText, (err) => { if (err) { console.error('保存文件失败:', err); return res.status(500).send('保存结果文件失败'); } res.set('Content-Type', 'text/plain'); res.send(resultText); }); // 无需保存到服务器的话,直接返回结果 // res.set('Content-Type', 'text/plain'); // res.send(resultText); } catch (err) { console.error('处理文件失败:', err); res.status(500).send('处理文件时发生错误'); } }); app.listen(3000, () => { console.log('API is listening on port 3000'); });
修复后的React代码(App.js片段)
const [inputFile, setInputFile] = useState(null); const [result, setResult] = useState(null); const [processingTime, setProcessingTime] = useState(null); const [error, setError] = useState(''); // 新增错误提示状态 const handleFileUpload = (event) => { setInputFile(event.target.files[0]); setError(''); // 选择文件后清空错误提示 }; const startCalculation = async (event) => { event.preventDefault(); // 检查是否选择文件 if (!inputFile) { setError('请先选择要上传的文件'); return; } const startTime = performance.now(); const formData = new FormData(); formData.append('inputFile', inputFile); try { const response = await fetch("http://localhost:3000/sort", { method: 'POST', body: formData, // 移除no-cors模式,允许跨域请求 }); if (!response.ok) { const errText = await response.text(); throw new Error(errText || '服务器处理失败'); } const data = await response.text(); setResult(data); setProcessingTime(performance.now() - startTime); setError(''); } catch (err) { console.error('请求失败:', err); setError(err.message); setResult(null); } }; const handleDownload = (event) => { event.preventDefault(); if (!result) return; const file = new Blob([result], { type: 'text/plain' }); const fileURL = URL.createObjectURL(file); const link = document.createElement('a'); link.href = fileURL; link.download = 'result.txt'; link.click(); // 释放URL对象,避免内存泄漏 URL.revokeObjectURL(fileURL); };
内容的提问来源于stack exchange,提问作者Simeon Jordanov
相关产品推荐
相关产品推荐

