前言:
前段时间在使用APS.NET MVC+LayUI做视频上传功能的时,发现当上传一些内存比较大的视频就会提示上传失败,后来通过查阅相关资料发现.NET MVC框架为考虑安全问题,在运行时对请求的文件的长度(大小)做了限制默认为4MB(4096KB),因此我们需要在Web.Config中设置最大请求文件长度大小,本篇博客主要讲解如何设置Web.Config中的最大请求文件大小配置和提供一个完整的ASP.NET MVC+LayUI上传视频的教程,并且会提供一个完整的示例(是上传到GitHub)有兴趣的可以耐心的往下看。
GitHub完整实例地址:
https://github.com/YSGStudyHards/VideoUpload
上传内存较大视频提示异常(HTTP Error 404.13 - Not Found):
异常原因分析:
由上图我们可以清楚的知道因为我们所上传的视频内容藏毒超过了配置的值,所以上传失败了,并且还告诉我们需要到web.config文件中配置允许最大上传的文件长度。
到web.config文件中的httpRuntime节点配置最大上传文件大小:
首先我们打开web.config=>找到system.web=>在httpRuntime中添加maxRequestLength属性值
如下所示(maxRequestLength根据需求设置):
httpRuntime代码解释:
executionTimeout:表示允许执行请求的最大时间限制,单位为秒。 maxRequestLength:指示 ASP.NET 支持的最大文件上载大小。该限制可用于防止因用户将大量文件传递到该服务器而导致的拒绝服务攻击。指定的大小以 KB 为单位。默认值为 4096 KB (4 MB)。 useFullyQualifiedRedirectUrl:表示指示客户端重定向是否是完全限定的(采用 "http://server/path" 格式,这是某些移动控件所必需的),或者指示是否代之以将相对重定向发送到客户端。如果为 True,则所有不是完全限定的重定向都将自动转换为完全限定的格式。false 是默认选项。 minFreeThreads:表示指定允许执行新请求的自由线程的最小数目。ASP.NET 为要求附加线程来完成其处理的请求而使指定数目的线程保持自由状态。默认值为 8。 minLocalRequestFreeThreads:表示ASP.NET 保持的允许执行新本地请求的自由线程的最小数目。该线程数目是为从本地主机传入的请求而保留的,以防某些请求在其处理期间发出对本地主机的子请求。这避免了可能的因递归重新进入 Web 服务器而导致的死锁。 appRequestQueueLimit:表示ASP.NET 将为应用程序排队的请求的最大数目。当没有足够的自由线程来处理请求时,将对请求进行排队。当队列超出了该设置中指定的限制时,将通过“503 - 服务器太忙”错误信息拒绝传入的请求。 enableVersionHeader:表示指定 ASP.NET 是否应输出版本标头。Microsoft Visual Studio 2005 使用该属性来确定当前使用的 ASP.NET 版本。对于生产环境,该属性不是必需的,可以禁用。
设置相关配置后视频上传成功,上传效果如下所示:
前端使用LayUI的视频组件提交视频以二进制的文件格式提交到后端服务接口:
1、首先引入相关的layUI相关的js和css文件包:
2、在页面中导入相关引用:
3、使用layer.js视频组件提交二进制文件到后端服务:
<link href="~/Content/layer-v3.1.1/layer/theme/default/layer.css" rel="stylesheet" /> <link href="~/Content/layui-v2.4.5/css/layui.css" rel="stylesheet" /> <div class="jumbotron" style="margin-top: 200px;"> <h3><a href="https://www.cnblogs.com/Can-daydayup/">追逐时光者的ASP.NET MVC+LayUI视频上传教程a>h3> <div class="row" style="margin-top: 20px;"> <div class="form-group znStyle"> <label class="col-sm-2 control-label"><em class="zent-form__required">*em>视频上传:label> <div class="col-sm-6"> <div id="upload_all_file"> <div class="layui-upload"> <button type="button" class="layui-btn" id="VideoBtn"><i class="layui-icon">i>上传视频button> <input type="hidden" name="Video" id="Video" /> <div class="layui-upload-list" id="videoPlay"> div> div> div> div> div> div> div> <script src="~/Content/layer-v3.1.1/layer/layer.js">script> <script src="~/Content/layui-v2.4.5/layui.js">script> <script type="text/javascript"> var upload; //上传图片 layui.use('upload', function () { upload = layui.upload; upload.render({ before: function () { layer.msg('视频努力上传中,请耐心等待...', { icon: 16, shade: 0.8, time: false }); }, elem: '#VideoBtn' , url: '@Url.Action("FileLoad","FileUpload")' , accept: 'video' //视频 , exts: 'mp4'//只允许上传的后缀(mp4文件) , done: function (res) { console.log(res); layer.closeAll(); layer.msg(res.msg); if (res.code == 1) { $("#Video").val(res.path); $("#videoPlay").html(' + res.path + '" type="video/mp4" />'); $("#videoPlay").show(); // 自动播放 $("#currentVideo")[0].play(); } } }); $(".layui-upload-list").on("click", "i", function () { $(this).parent().remove(); }); }); script>
统一文件,图片,视频,音频上传服务(FileUploadController):
/** * Authority:追追时光者 * CreateTime:2020.08.01 * Description:文件,图片,视频,音频统一上传接口 */ using System; using System.IO; using System.Text; using System.Web; using System.Web.Mvc; namespace VideoUpload.Controllers { ////// 文件,图片,视频,音频统一上传服务 /// public class FileUploadController : Controller { /// /// 对验证和处理 HTML 窗体中的输入数据所需的信息进行封装,如FromData拼接而成的文件[图片,视频,文档等文件上传] /// /// FemContext对验证和处理html窗体中输入的数据进行封装 /// [AcceptVerbs(HttpVerbs.Post)] public ActionResult FileLoad(FormContext context)//FemContext对验证和处理html窗体中输入的数据进行封装 { HttpPostedFileBase httpPostedFileBase = Request.Files[0];//获取文件流 if (httpPostedFileBase != null) { try { ControllerContext.HttpContext.Request.ContentEncoding = Encoding.GetEncoding("UTF-8"); ControllerContext.HttpContext.Response.Charset = "UTF-8"; string fileName = Path.GetFileName(httpPostedFileBase.FileName);//原始文件名称 string fileExtension = Path.GetExtension(fileName);//文件扩展名 byte[] fileData = ReadFileBytes(httpPostedFileBase);//文件流转化为二进制字节 string result = SaveFile(fileExtension, fileData);//文件保存 return string.IsNullOrEmpty(result) ? Json(new { code = 0, path = "", msg = "网络异常,文件上传失败~" }) : Json(new { code = 1, path = result, msg = "文件上传成功" }); } catch (Exception ex) { return Json(new { code = 0, msg = ex.Message, path = "" }); } } else { return Json(new { code = 0, path = "", msg = "网络异常,文件上传失败~" }); } } /// /// 将文件流转化为二进制字节 /// /// 图片文件流 /// private byte[] ReadFileBytes(HttpPostedFileBase fileData) { byte[] data; using (var inputStream = fileData.InputStream) { if (!(inputStream is MemoryStream memoryStream)) { memoryStream = new MemoryStream(); inputStream.CopyTo(memoryStream); } data = memoryStream.ToArray(); } return data; } /// /// 保存文件 /// /// 文件扩展名 /// 图片二进制文件信息 /// private string SaveFile(string fileExtension, byte[] fileData) { string result; string saveName = Guid.NewGuid().ToString() + fileExtension; //保存文件名称 string basePath = "UploadFile"; string saveDir = DateTime.Now.ToString("yyyy-MM-dd"); // 文件上传后的保存路径 string serverDir = Path.Combine(Server.MapPath("~/"), basePath, saveDir); string fileNme = Path.Combine(serverDir, saveName);//保存文件完整路径 try { var savePath = Path.Combine(saveDir, saveName); //项目中是否存在文件夹,不存在创建 if (!Directory.Exists(serverDir)) { Directory.CreateDirectory(serverDir); } System.IO.File.WriteAllBytes(fileNme, fileData);//WriteAllBytes创建一个新的文件,按照对应的文件流写入,假如已存在则覆盖 //返回前端项目文件地址 result = "/" + basePath + "/" + saveDir + "/" + saveName; } catch (Exception ex) { result = "发生错误" + ex.Message; } return result; } } }
参考文章:
https://docs.microsoft.com/en-us/iis/configuration/system.webServer/security/requestFiltering/requestLimits/
http://www.webkaka.com/tutorial/asp.net/2018/051026/