如何不使用FormData,通过JavaScript模型传递HttpPostedFileBase类型参数
核心问题原因
你当前代码拿不到Documents参数是必然的:JSON 协议本身不支持序列化二进制类型,浏览器中的File对象属于二进制数据,JSON.stringify()处理时会直接转为空对象{},后端自然无法完成模型绑定。
你的两个疑问解答
- 完全不使用
FormData传文件是可以实现的,需要将文件转成Base64字符串后再纳入JS模型传递,适合小文件场景。 - 不需要给每个类都单独写
HttpPostedFileBase属性,要么转Base64后把文件字段改成字符串数组,要么用基类封装公共文件属性继承即可。
方案1:无FormData、Base64传文件(适配JS模型传递需求)
适合小文件场景,完全符合你想要的JS模型传参逻辑,不需要调整请求头为form-data。
修改步骤
- 调整C#模型,将文件接收字段改为字符串数组:
public class ArtGallery { // 注意public首字母要小写,你原代码的Public是语法错误 public string GallerName { get; set; } // 存储文件的Base64字符串 public string[] Documents { get; set; } }
- 调整JS代码,先将文件转成Base64再塞入模型:
class ArtGallery{ GallerName; Documents = []; // 必须初始化数组,否则push会报错 } function saveFormData(){ let gallery = new ArtGallery(); gallery.GallerName = "Test"; // 统计待处理文件总数,确保所有文件转码完成后再提交 let totalFiles = $("input[type=file]").map((i, obj) => obj.files.length).get().reduce((a, b) => a + b, 0); let processedCount = 0; $.each($("input[type=file]"), function (i, obj) { $.each(obj.files, function (j, file) { const reader = new FileReader(); reader.onload = function(e) { // 转码完成后将Base64字符串塞入模型 gallery.Documents.push(e.target.result); processedCount++; if (processedCount === totalFiles) { saveToDb(gallery); } } reader.readAsDataURL(file); }); }); } function saveToDb(gallery) { // 注意URL要和控制器动作名对应,你原动作是PostUserData,不是PostData const url = "/MyController/PostUserData/"; $.ajax({ url: url, type: 'POST', async: true, // 不要用async:false,同步请求会阻塞页面,已被主流浏览器弃用 data: JSON.stringify({ gallery: gallery }), contentType: 'application/json; charset=utf-8', dataType: 'json', success: function (result) { // 业务逻辑 }, error: function (request) { // 异常处理 } }); }
- 后端处理Base64字符串转文件:
[HttpPost] public JsonResult PostUserData(ArtGallery gallery) { foreach (var base64File in gallery.Documents) { // 移除Base64前缀(如data:image/png;base64,) var pureBase64 = base64File.Split(',')[1]; byte[] fileBytes = Convert.FromBase64String(pureBase64); // 后续按需存本地、存OSS等处理 } // 其余业务逻辑 }
该方案缺点:Base64编码后体积比原文件大33%左右,大文件上传会增加带宽消耗、延长上传时间,不适合10M以上的文件传输。
方案2:FormData通用封装(适合大文件场景)
如果需要传大文件,还是推荐用FormData,只需封装一层通用的模型转FormData逻辑,不需要每个接口重复写代码,同时不需要修改原有C#模型:
通用封装+调用示例
// 通用函数:任意JS模型转FormData function modelToFormData(model) { const formData = new FormData(); for (const key in model) { const value = model[key]; // 数组类型处理 if (Array.isArray(value)) { for (let i = 0; i < value.length; i++) { formData.append(`${key}[${i}]`, value[i]); } continue; } formData.append(key, value); } return formData; } // 调用时只需改saveToDb方法即可 function saveToDb(gallery) { const url = "/MyController/PostUserData/"; const formData = modelToFormData(gallery); $.ajax({ url: url, type: 'POST', processData: false, // 必须配置,禁止jQuery处理FormData contentType: false, // 必须配置,禁止jQuery自动设置请求头 data: formData, success: function (result) { // 业务逻辑 }, error: function (request) { // 异常处理 } }); }
解决10个类重复写HttpPostedFileBase的问题
只需封装公共基类,所有需要传文件的类继承即可,不用重复写属性:
// 公共基类 public class BaseFileUploadModel { public HttpPostedFileBase[] Documents { get; set; } } // 业务类继承基类即可,不用重复写Documents public class ArtGallery : BaseFileUploadModel { public string GallerName { get; set; } } // 其余9个需要传文件的类同理继承BaseFileUploadModel
其他注意事项
- C#语法中访问修饰符
public首字母为小写,你原代码中写的Public会编译报错,也会导致模型绑定失败 - 不要使用
async: false配置,同步AJAX会阻塞整个页面交互,W3C已经在标准中废弃同步XMLHttpRequest的使用 - AJAX请求的URL要和后端控制器动作名一致,你原代码中控制器动作是
PostUserData,请求URL写的PostData会返回404错误
内容的提问来源于stack exchange,提问作者Learner
相关产品推荐
相关产品推荐

