文件类型字段远程验证无法接收字段值问题
解决ASP.NET MVC远程验证无法接收文件字段的问题
你遇到的问题很典型——默认的RemoteAttribute是通过GET请求发送验证数据的,而文件(HttpPostedFileBase)既没法通过GET传递,也不能被模型绑定到GET参数里。另外,就算改成POST请求,默认的jQuery Validate远程验证逻辑也不会自动把文件数据打包发送到后台。
下面给你两种可行的解决方案,推荐第一种(更高效):
方案1:通过文件哈希值做远程验证(推荐)
核心思路是:前端计算选中文件的唯一哈希值(比如MD5),把哈希值传到后台验证是否已存在,避免传输整个文件浪费带宽。
步骤1:修改模型
新增一个用于存储文件哈希的字段,把Remote验证标记移到这个字段上:
public class FileUpload { [Display(Name = "Archivo")] [Required(ErrorMessage = "Debe seleccionar un archivo.")] public HttpPostedFileBase File { get; set; } [Display(Name = "Test")] [Required(ErrorMessage = "Deb...")] public string Test { get; set; } // 新增字段:存储文件哈希,用于远程验证 [Remote("BackupFileExist", "ImportJobs", HttpMethod = "POST", ErrorMessage = "El archivo ya fue cargado.")] public string FileHash { get; set; } }
步骤2:前端计算文件哈希
用JavaScript读取文件内容并计算MD5哈希(需要引入第三方库,比如spark-md5),然后把哈希值赋值给隐藏的FileHash字段,触发验证:
<!-- 在视图里添加隐藏字段 --> @Html.HiddenFor(m => m.FileHash) <script src="https://cdn.jsdelivr.net/npm/spark-md5@3.0.2/spark-md5.min.js"></script> <script> $(document).ready(function() { // 监听文件选择事件 $('#File').change(function(e) { const file = e.target.files[0]; if (!file) { $('#FileHash').val(''); return; } const reader = new FileReader(); reader.onload = function(event) { // 计算文件MD5哈希 const hash = SparkMD5.ArrayBuffer.hash(event.target.result); $('#FileHash').val(hash); // 手动触发远程验证 $('#FileHash').valid(); }; reader.readAsArrayBuffer(file); }); }); </script>
步骤3:后台验证Action
接收哈希值,查询数据库判断是否已存在相同哈希的文件:
[HttpPost] public JsonResult BackupFileExist(string fileHash) { // 替换成你的数据库查询逻辑 bool fileExists = YourDbContext.UploadedFiles.Any(f => f.FileHash == fileHash); // 远程验证规则:返回true表示验证通过(文件未上传),false表示验证失败 return Json(!fileExists); }
方案2:直接传递文件到验证Action(不推荐大文件)
如果不想计算哈希,可以修改远程验证为POST请求,并重写jQuery Validate的逻辑来发送文件FormData:
步骤1:修改模型的Remote属性
指定HttpMethod为POST:
public class FileUpload { [Display(Name = "Archivo")] [Required(ErrorMessage = "Debe seleccionar un archivo.")] [Remote("BackupFileExist", "ImportJobs", HttpMethod = "POST", ErrorMessage = "El archivo ya fue cargado.")] public HttpPostedFileBase File { get; set; } [Display(Name = "Test")] [Required(ErrorMessage = "Deb...")] public string Test { get; set; } }
步骤2:重写jQuery Validate的remote方法
让它支持发送文件FormData:
$.validator.methods.remote = function(value, element, param) { if (this.optional(element)) { return "dependency-mismatch"; } const previous = this.previousValue(element); // 处理文件字段的情况 if (!previous && element.type === "file") { const formData = new FormData(); formData.append(element.name, element.files[0]); $.ajax({ url: typeof param === "string" ? param : param.url, type: param.type || "POST", data: formData, processData: false, contentType: false, async: true, dataType: "json", success: function(response) { const valid = response === true || response === "true"; $.validator.methods.remote.call(this, valid ? "true" : "false", element, param); previous.valid = valid; previous.message = response.message || this.defaultMessage(element, "remote"); this.showErrors(); }.bind(this) }); return "pending"; } // 保留默认的非文件字段验证逻辑 if (!this.settings.messages[element.name]) { this.settings.messages[element.name] = {}; } previous.originalMessage = previous.originalMessage || this.settings.messages[element.name].remote; this.settings.messages[element.name].remote = previous.message; param = typeof param === "string" ? { url: param } : param; if (previous.old === value) { return previous.valid; } previous.old = value; const validator = this; this.startRequest(element); const data = {}; data[element.name] = value; $.ajax($.extend(true, { mode: "abort", port: "validate" + element.name, dataType: "json", data: data, context: validator.currentForm, success: function(response) { const valid = response === true || response === "true"; if (valid) { const submitted = validator.formSubmitted; validator.prepareElement(element); validator.formSubmitted = submitted; validator.successList.push(element); delete validator.invalid[element.name]; validator.showErrors(); } else { const errors = {}; const message = response.message || validator.defaultMessage(element, "remote"); errors[element.name] = previous.message = message; validator.invalid[element.name] = true; validator.showErrors(errors); } previous.valid = valid; validator.stopRequest(element, valid); } }, param)); return "pending"; };
步骤3:后台验证Action
接收文件,计算哈希后判断是否已上传:
[HttpPost] public JsonResult BackupFileExist(HttpPostedFileBase file) { if (file == null || file.ContentLength == 0) { // 文件为空时,Required属性已经会验证,这里返回true跳过远程验证 return Json(true); } // 计算文件MD5哈希 string fileHash; using (var md5 = MD5.Create()) { fileHash = BitConverter.ToString(md5.ComputeHash(file.InputStream)) .Replace("-", "") .ToLower(); // 重置流位置,避免后续读取文件出错 file.InputStream.Position = 0; } // 查询数据库判断是否已存在 bool fileExists = YourDbContext.UploadedFiles.Any(f => f.FileHash == fileHash); return Json(!fileExists); }
内容的提问来源于stack exchange,提问作者Juan Carlos Oropeza
相关产品推荐
相关产品推荐

