使用Retrofit 2+Node.js+Multer上传文件时req.body为空求助
Retrofit2 + Node.js/Multer 文件上传 req.body 为空问题解决
问题描述
使用Retrofit 2、Node.js、Express和Multer实现文件上传功能,文件可成功上传,但服务器端req.body始终为空,导致文件被上传到名为undefined的文件夹,文件名显示为「当前时间戳-undefined」。尝试将Android端createPartFromString方法中的RequestBody类型改为text/plain,问题依旧。
相关代码
Android端Retrofit代码
上传方法
private suspend fun uploadNewFiles( context: Context, files: List<FileData>, productModel: ProductModel ): Response<ProductModel> { val part = MultiPartUtils.prepareFilePart(context, "file", files[0].uri) val fileName = "${productModel.p_id.toString()}-${productModel.p_nm}" val fileNameRequestBody: RequestBody = MultiPartUtils.createPartFromString(fileName) val folderNameRequestBody: RequestBody = MultiPartUtils.createPartFromString("products") return productAPI.uploadSingleFile(part, fileNameRequestBody, folderNameRequestBody) }
工具类
object MultiPartUtils { fun createPartFromString(descriptionString: String): RequestBody { return descriptionString.toRequestBody(MultipartBody.FORM) } fun prepareFilePart(context: Context, partName: String, fileUri: Uri): MultipartBody.Part { // 通过Uri获取实际文件 val file: File = FileUtils.getFileNew(fileUri, context) // 从文件创建RequestBody val requestFile: RequestBody = file .asRequestBody(context.contentResolver.getType(fileUri)?.toMediaTypeOrNull()) // 创建包含文件名的MultipartBody.Part return MultipartBody.Part.createFormData(partName, file.name, requestFile) } }
Retrofit接口
@Multipart @POST("/products/upload-products") suspend fun uploadSingleFile( @Part ImageToUpload: MultipartBody.Part, @Part("fileName") fileName: RequestBody, @Part("folderName") folderName: RequestBody ): Response<ProductModel>
Node.js端代码
上传控制器
exports.uploadSingle = async (req, res) => { console.log("upload-1") const uploadFile = util.promisify(upload.single('file')); try { await uploadFile(req, res); res.status(200).send({message:"File Uploaded"}) } catch (error) { res.status(401).json({message:"Error while uploading!"}) } }
Multer配置
const multer = require('multer'); const path = require('path'); const fs = require('fs-extra'); const storage = multer.diskStorage({ destination: (req, file, cb) => { console.log(req.body) // 此处输出为空 const { folderName } = req.body const path = `public/images/${folderName}/`; fs.mkdirSync(path, { recursive: true }) cb(null, path) }, filename: (req, file, cb) => { const { fileName } = req.body cb(null, Date.now()+"-"+fileName + path.extname(file.originalname)) }, limits: { fileSize: 10000000 }, fileFilter: function (req, file, cb) { if (!file.originalname.match(/\.(jpg|JPG|webp|jpeg|JPEG|png|PNG|gif|GIF|jfif|JFIF)$/)) { req.fileValidationError = 'Only image files are allowed!'; return cb(null, false); } cb(null, true); } }); const upload = multer({ storage: storage }); module.exports = upload;
解决方案
核心原因
Multer在处理diskStorage的destination和filename回调时,表单字段的发送顺序直接影响req.body的解析时机:如果文件Part先于文本字段发送,Multer会优先处理文件,此时文本字段尚未被解析,req.body自然为空。你的Android代码中先构造并发送了文件Part,导致Node端无法获取到fileName和folderName。
修复步骤
调整Android端Part的发送顺序
将文本字段(fileName、folderName)放在文件Part之前发送,确保Multer先解析文本字段:- 修改上传方法的调用顺序:
return productAPI.uploadSingleFile( fileNameRequestBody, folderNameRequestBody, part // 文件Part移到最后 ) - 同步更新Retrofit接口的参数顺序:
@Multipart @POST("/products/upload-products") suspend fun uploadSingleFile( @Part("fileName") fileName: RequestBody, @Part("folderName") folderName: RequestBody, @Part ImageToUpload: MultipartBody.Part ): Response<ProductModel>
- 修改上传方法的调用顺序:
验证调试
在Node端的destination回调中添加日志,确认req.body是否正常获取到字段:destination: (req, file, cb) => { console.log("req.body content:", req.body); // 现在应能看到fileName和folderName const { folderName } = req.body; const filePath = `public/images/${folderName}/`; fs.mkdirSync(filePath, { recursive: true }); cb(null, filePath); }
注意事项
- 无需修改
createPartFromString方法的RequestBody类型,保持MultipartBody.FORM即可,这是Multipart表单的标准类型。 - 确保Node端Express中间件配置正确,不需要额外添加
body-parser处理Multipart表单,Multer会自动解析。
内容的提问来源于stack exchange,提问作者user2659167
相关产品推荐
相关产品推荐

