使用socket.io+FormData传文件报错:Cannot read property 'transfer-encoding'
问题描述
尝试用socket.io实现图片传输时遇到如下错误:
return req.headers['transfer-encoding'] !== undefined ||
TypeError: Cannot read property 'transfer-encoding' of undefined
此前使用axios+multer时可以正常运行,以下是服务端和客户端代码,请问哪里出错了?
服务端代码
const fileSchema = new mongoose.Schema({ originalname: String, filename: String, path: String, mimetype: String, }); const File = mongoose.model("File", fileSchema); socket.on("ROOM:FILE_UPLOAD", (formData) => { upload.single("image")(formData, null, (err) => { if (err) console.log(err); else { const originalname = formData.originalname; const filename = formData.filename; const path = formData.path; const mimetype = formData.mimetype; const file = new File({ originalname, filename, path, mimetype, }); file.save((err, image) => { if (err) { console.error(err); } else { console.log(`Image saved to MongoDB: ${image._id}`); } }); } }); });
客户端代码
const [file, setFile] = useState(null); function handleFileSelect(event: any) { setFile(event.target.files[0]); } function sendMsg(event) { event.preventDefault(); const formData = new FormData(); formData.append("image", file); socket.emit("ROOM:FILE_UPLOAD", formData); } return ( <div className="container"> <div className="row"> <form encType="multipart/form-data" onSubmit={upload}> <input type="file" accept="image/*" onChange={handleFileSelect} name="image" /> <button onClick={sendMsg}>send</button> </form> </div> </div> );
问题原因
multer是为Express的HTTP请求设计的中间件,它依赖标准的Express req对象(包含headers、body等HTTP请求属性)。但socket.io传输的formData并不是HTTP请求对象,只是普通JavaScript对象,没有headers属性,所以multer执行时会抛出Cannot read property 'transfer-encoding' of undefined错误。
之前用axios+multer正常是因为axios发送的是标准HTTP请求,multer能拿到完整的req对象。
解决方案
放弃用multer处理socket.io的文件传输,直接通过socket.io传输文件二进制数据,服务端手动处理文件保存和数据库存储。
修改后的客户端代码
const [file, setFile] = useState(null); function handleFileSelect(event: any) { setFile(event.target.files[0]); } function sendMsg(event) { event.preventDefault(); if (!file) return; const reader = new FileReader(); reader.onload = () => { // 发送文件元数据和二进制数据 socket.emit("ROOM:FILE_UPLOAD", { originalname: file.name, mimetype: file.type, buffer: reader.result // ArrayBuffer格式 }); }; reader.readAsArrayBuffer(file); } return ( <div className="container"> <div className="row"> <form onSubmit={sendMsg}> <input type="file" accept="image/*" onChange={handleFileSelect} name="image" /> <button type="submit">send</button> </form> </div> </div> );
修改后的服务端代码
const fs = require('fs'); const path = require('path'); const { v4: uuidv4 } = require('uuid'); // 生成唯一文件名 const fileSchema = new mongoose.Schema({ originalname: String, filename: String, path: String, mimetype: String, }); const File = mongoose.model("File", fileSchema); socket.on("ROOM:FILE_UPLOAD", async (fileData) => { try { // 生成唯一文件名,避免重名 const filename = `${uuidv4()}_${fileData.originalname}`; // 定义文件保存路径(确保uploads目录存在) const savePath = path.join(__dirname, 'uploads', filename); // 将ArrayBuffer转成Buffer并写入文件 fs.writeFileSync(savePath, Buffer.from(fileData.buffer)); // 存入MongoDB const file = new File({ originalname: fileData.originalname, filename, path: savePath, mimetype: fileData.mimetype, }); const savedFile = await file.save(); console.log(`Image saved to MongoDB: ${savedFile._id}`); } catch (err) { console.error('文件保存失败:', err); } });
额外注意事项
- 确保服务端的
uploads目录存在,否则需要先创建(可以用fs.mkdirSync(path.join(__dirname, 'uploads'), { recursive: true }))。 - 如果文件较大,建议用socket.io的分片传输,避免单次传输过大数据导致性能问题。
内容的提问来源于stack exchange,提问作者Daniil Galitskii
相关产品推荐
相关产品推荐

