向Express服务器上传文件时仅视频出现TypeError/NetworkError异常
解决重复上传视频文件时的NetworkError问题
问题根源
当检测到视频文件已存在并返回409状态码后,服务器没有终止对上传文件流的处理。视频文件体积较大,浏览器仍在持续上传数据,但服务器已经发送了响应,导致连接被强制中断,最终触发NetworkError。而图片文件体积小,可能在服务器返回响应前就完成了数据上传,因此不会出现该问题。
解决方案
1. 服务器端:终止文件流并正确结束响应
在检测到文件已存在时,不仅要返回409状态码,还要手动终止file流的读取,避免后续数据继续传输,同时确保响应完全结束。另外,将res.redirect('back')改为返回明确的状态码,避免fetch请求处理重定向的异常。
修改后的服务器代码:
var express = require("express"); var busboy = require('connect-busboy'); var cors = require('cors'); var fs = require('fs'); var path = require('path'); var app = express(); app.use(busboy()); app.use(cors()); app.use(express.static(path.join(__dirname, 'public'))); app.route('/upload') .post(function (req, res, next) { req.pipe(req.busboy); req.busboy.on('file', function (fieldname, file, filename) { const filePath = path.join(__dirname, 'public/uploaded', filename.filename); if (fs.existsSync(filePath)) { // 终止文件流读取,避免内存泄漏 file.resume(); return res.sendStatus(409); } else { console.log("Uploading: " + filename.filename); const fstream = fs.createWriteStream(filePath, {flags: "w+"}); file.pipe(fstream); fstream.on('close', function () { console.log("Upload Finished of " + filename.filename); res.sendStatus(200); }); // 捕获文件写入错误 fstream.on('error', function(err) { console.error('文件写入错误:', err); res.sendStatus(500); }); } }); // 捕获Busboy处理错误 req.busboy.on('error', function(err) { console.error('Busboy错误:', err); res.sendStatus(500); }); }); var server = app.listen(4200, function() { console.log('Listening on port %d', server.address().port); });
2. 前端:完善fetch的错误处理
fetch API不会对HTTP错误状态码(如409)触发reject,需要手动检查响应状态,并添加网络错误的捕获逻辑:
修改后的前端index.js代码:
document.getElementById("upload").onchange = function() { let photo = document.getElementById("upload").files[0]; let formData = new FormData(); formData.append("photo", photo); fetch("/upload", {method: "POST", body: formData}) .then(response => { if (response.status === 409) { alert("文件已存在"); } else if (!response.ok) { throw new Error(`请求失败: ${response.status}`); } else { alert("上传成功"); } }) .catch(error => { console.error('上传出错:', error); alert("上传失败,请重试"); }); }
额外优化建议
- 使用异步IO替代同步IO,避免阻塞服务器线程:
// 替换原有的fs.existsSync判断 try { await fs.promises.access(filePath, fs.constants.F_OK); file.resume(); return res.sendStatus(409); } catch { // 文件不存在,执行上传逻辑 } - 给上传按钮添加loading状态,防止用户重复提交。
内容的提问来源于stack exchange,提问作者SanguineL
相关产品推荐
相关产品推荐

