第六节:基于LayUI组件的文件上传 和 基于dropzonejs的文件上传
一. 接口设计
1. 说明
设计异步方法,这里采用文件流的形式进行存储,设计两个接口,分别用来处理单文件上传和多文件上传.
2. 大致思路
获取文件→判空→获取文件名和扩展名→设置存放绝对路径(若不存在,则新建)→编辑文件保存名称(这里随机命名,所以不用判重) →拼接最终路径进行保存→DB中存储相对路径→返回前端成功和相对路径.
3. 其它
可以通过 .Length,来获取文件的大小
代码分享:

#region 01-单文件上传 /// <summary> /// 单文件上传 /// </summary> /// <param name="_hostingEnvironment"></param> /// <returns></returns> public async Task<IActionResult> SingleSaveFile([FromServices] IWebHostEnvironment _hostingEnvironment) { try { long size = 0; //统计上传文件的大小(单位b) var files = Request.Form.Files; //获取文件 if (files.Count == 0) { return Json(new { status = "error", msg = "上传内容为空", data = "" }); } //获取文件扩展名 var fileName = files[0].FileName; int idxStart = fileName.LastIndexOf("."); string areviation = fileName.Substring(idxStart, fileName.Length - idxStart); //编辑文件的存储路径 var filePath = _hostingEnvironment.ContentRootPath + @"\DownLoad\Picture\"; if (!Directory.Exists(filePath)) { Directory.CreateDirectory(filePath); } //编辑文件的名称(目前是随机命名,如果用原名保存,需要判重) var myFileName = $"{Guid.NewGuid().ToString()}{areviation}"; //最终路径 var finalPath = filePath + myFileName; size += files[0].Length; using (FileStream fs = System.IO.File.Create(finalPath)) { await files[0].CopyToAsync(fs); await fs.FlushAsync(); } //DB中存储的或者返回给前端的都是相对路径 string relativeUrl = $"/DownLoad/Picture/{myFileName}"; return Json(new { status = "ok", msg = "上传成功", data = relativeUrl }); } catch (Exception ex) { return Json(new { status = "error", msg = "上传失败", data = "" }); }; } #endregion #region 02-多文件上传 /// <summary> /// 多文件上传 /// </summary> /// <param name="_hostingEnvironment"></param> /// <returns></returns> public async Task<IActionResult> ManySaveFile([FromServices] IWebHostEnvironment _hostingEnvironment) { try { long size = 0; //统计上传文件的大小(单位b) var files = Request.Form.Files; //获取文件 if (files.Count == 0) { return Json(new { status = "error", msg = "上传内容为空", data = "" }); } List<string> rUrlList = new List<string>(); //多文件遍历上传 foreach (var file in files) { //获取文件扩展名 var fileName = file.FileName; int idxStart = fileName.LastIndexOf("."); string areviation = fileName.Substring(idxStart, fileName.Length - idxStart); //编辑文件的存储路径 var filePath = _hostingEnvironment.ContentRootPath + @"\DownLoad\Picture\"; if (!Directory.Exists(filePath)) { Directory.CreateDirectory(filePath); } //编辑文件的名称(目前是随机命名,如果用原名保存,需要判重) var myFileName = $"{Guid.NewGuid().ToString()}{areviation}"; //最终路径 var finalPath = filePath + myFileName; size += file.Length; using (FileStream fs = System.IO.File.Create(finalPath)) { await file.CopyToAsync(fs); await fs.FlushAsync(); } //DB中存储的或者返回给前端的都是相对路径 string relativeUrl = $"/DownLoad/Picture/{myFileName}"; rUrlList.Add(relativeUrl); } return Json(new { status = "ok", msg = "上传成功", data = rUrlList }); } catch (Exception ex) { return Json(new { status = "error", msg = "上传失败", data = "" }); }; } #endregion
二. 基于LayUI文件上传
1.相关地址
官方文档:https://www.layui.com/doc/modules/upload.html
官方样例:https://www.layui.com/demo/upload.html
2. 各种样例
(1). 单文件上传
A.基础配置:Header表头、data参数、acceptMime筛选文件类型、accept+exts允许上传的文件和后缀、Size最大上传大小等
B.几个回调:choose选择文件后回调、before文件提交前回调、done上传成功后回调、error请求异常回调
代码分享:

@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>文件上传</title> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0"> <link href="~/src/layuiadmin/layui/css/layui.css" rel="stylesheet" /> <link href="~/src/layuiadmin/style/admin.css" rel="stylesheet" /> <style> .c1 { width: 500px; height: 280px; border: 1px solid black; padding: 20px; float: left; } img { width: 200px; height: 200px; } </style> </head> <body> <div class="c1"> <button type="button" class="layui-btn" id="test1"> 单文件上传(各种属性) </button> <br /> <div> <img src="/DownLoad/Picture/demo.png" id="j_img1" /> </div> </div> <script src="~/src/layuiadmin/layui/layui.js"></script> <script> layui.use(['upload', 'jquery'], function () { var $ = layui.$; var upload = layui.upload; //1.单文件上传 var uploadInst1 = upload.render({ elem: '#test1', //绑定元素 url: 'SingleSaveFile', //上传接口 headers: { auth: window.localStorage.getItem("token") }, //表头 data: {}, //额外参数 acceptMime: 'image/*', //选择框筛选文件类型(不是很准确) accept: "file", //允许上传所有文件 //exts:"zip|rar|7z", //允许上传的后缀的类型,和accept配合使用 size: 0, //设置文件最大可允许上传的大小,单位 KB, 0表示不限制 choose: function (obj) { //选择文件后回调 }, before: function () { //文件提交前回调 layer.load(); //上传loading }, done: function (res) { //上传成功后回调 if (res.status == "ok") { alert(res.msg); $("#j_img1").attr("src", res.data); layer.closeAll('loading'); //关闭loading } }, error: function (index, upload) { //请求异常回调 layer.closeAll('loading'); //关闭loading } }); }); </script> </body> </html>
运行效果:
(2).多文件上传
A. 原理
LayUI的多文件上传只是一次可以选多个文件而已,是通过调用多次接口实现的,目前没有实现调用一次接口,所以这里还是调用SingleSaveFile,对于前端而言done回调每成功1个文件回调1次, allDone当所有文件提交后才能被触发,会返回文件总数、成功文件数、失败文件数。
B. 核心配置
(1). multiple: true, //允许多文件上传
(2). number: 2, //允许上传的文件数量,配合multiple使用
代码分享:

@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>文件上传</title> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0"> <link href="~/src/layuiadmin/layui/css/layui.css" rel="stylesheet" /> <link href="~/src/layuiadmin/style/admin.css" rel="stylesheet" /> <style> .c1 { width: 500px; height: 280px; border: 1px solid black; padding: 20px; float: left; } img { width: 200px; height: 200px; } </style> </head> <body> <div class="c1"> <button type="button" class="layui-btn" id="test2"> 多文件上传 </button> <br /> <div id="j2"> </div> </div> <script src="~/src/layuiadmin/layui/layui.js"></script> <script> layui.use(['upload', 'jquery'], function () { var $ = layui.$; var upload = layui.upload; //2.多文件上传 var uploadInst2 = upload.render({ elem: '#test2', //绑定元素 url: 'SingleSaveFile', //上传接口 headers: { auth: window.localStorage.getItem("token") }, //表头 data: {}, //额外参数 acceptMime: 'image/*', //选择框筛选文件类型(不是很准确) accept: "file", //允许上传所有文件 //exts:"zip|rar|7z", //允许上传的后缀的类型,和accept配合使用 size: 0, //设置文件最大可允许上传的大小,单位 KB, 0表示不限制 multiple: true, //允许多文件上传 number: 2, //允许上传的文件数量,配合multiple使用 choose: function (obj) { //选择文件后回调 }, before: function () { //文件提交前回调 layer.load(); //上传loading }, allDone: function (obj) { //当文件全部被提交后,才触发 console.log(obj.total); //得到总文件数 console.log(obj.successful); //请求成功的文件数 console.log(obj.aborted); //请求失败的文件数 layer.closeAll('loading'); //关闭loading }, done: function (res) { //上传成功后回调(每成功一个文件,回调一次) if (res.status == "ok") { $("#j2").append('<img src="' + res.data + '"/>'); } }, error: function (index, upload) { //请求异常回调 //当上传失败时,你可以生成一个“重新上传”的按钮,点击该按钮时,执行 upload() 方法即可实现重新上传 layer.closeAll('loading'); //关闭loading } }); }); </script> </body> </html>
运行效果:
(3).非自动上传+队列
大致流程:
通过 auto: false设置不自动上传,然后通过 bindAction: '#test33' 指向一个按钮绑定上传,选择文件后进入choose回调,将文件存放到本地队列中,并且可以操作DOM,用于提前预览,上传成功后,修改本地DOM或者删除; 上传失败后,将本地DOM改为重新上传。
代码分享:

@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>文件上传</title> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0"> <link href="~/src/layuiadmin/layui/css/layui.css" rel="stylesheet" /> <link href="~/src/layuiadmin/style/admin.css" rel="stylesheet" /> <style> .c1 { width: 500px; height: 280px; border: 1px solid black; padding: 20px; float: left; } img { width: 200px; height: 200px; } </style> </head> <body> <div class="c1"> <button type="button" class="layui-btn" id="test3"> 非自动上传+队列 </button> <button type="button" class="layui-btn" id="test33"> 点我上传 </button> <br /> <div id="j3"> </div> </div> <script src="~/src/layuiadmin/layui/layui.js"></script> <script> layui.use(['upload', 'jquery'], function () { var $ = layui.$; var upload = layui.upload; //3.非自动上传+队列 var uploadInst3 = upload.render({ elem: '#test3', //绑定元素 url: 'SingleSaveFile', //上传接口 headers: { auth: window.localStorage.getItem("token") }, //表头 data: {}, //额外参数 acceptMime: 'image/*', //选择框筛选文件类型(不是很准确) accept: "file", //允许上传所有文件 //exts:"zip|rar|7z", //允许上传的后缀的类型,和accept配合使用 size: 0, //设置文件最大可允许上传的大小,单位 KB, 0表示不限制 multiple: true, //允许多文件上传 number: 2, //允许上传的文件数量,配合multiple使用 auto: false, //选择文件后不自动上传 bindAction: '#test33', //指向一个按钮触发上传 choose: function (obj) { //选择文件后回调 //将每次选择的文件追加到文件队列 var files = obj.pushFile(); //预读本地文件,如果是多文件,则会遍历。(不支持ie8/9) obj.preview(function (index, file, result) { console.log(index); //得到文件索引 console.log(file); //得到文件对象 //console.log(result); //得到文件base64编码,比如图片 //obj.resetFile(index, file, '123.jpg'); //重命名文件名,layui 2.3.0 开始新增 //这里还可以做一些 append 文件列表 DOM 的操作 var tr = $(['<tr id="upload-' + index + '">' , '<td>' + file.name + '</td>' , '<td>' + (file.size / 1024).toFixed(1) + 'kb</td>' , '<td>等待上传</td>' , '<td>' , '<button class="layui-btn layui-btn-xs demo-reload layui-hide">单个重传</button>' , '<button class="layui-btn layui-btn-xs layui-btn-danger demo-delete">删除</button>' , '</td>' , '</tr>'].join('')); //单个重传(上传失败的时候配置成显示) tr.find('.demo-reload').on('click', function () { obj.upload(index, file); }); //删除 tr.find('.demo-delete').on('click', function () { delete files[index]; //删除对应的文件 tr.remove(); uploadInst3.config.elem.next()[0].value = ''; //清空 input file 值,以免删除后出现同名文件不可选 }); $("#j3").append(tr); //obj.upload(index, file); //对上传失败的单个文件重新上传,一般在某个事件中使用 //delete files[index]; //删除列表中对应的文件,一般在某个事件中使用 }); }, before: function () { //文件提交前回调 //layer.load(); //上传loading }, allDone: function (obj) { //当文件全部被提交后,才触发 }, done: function (res) { //上传成功后回调(每成功一个文件,回调一次) if (res.status == "ok") { $("#j3").append('<img src="' + res.data + '"/>'); //删除列表中待上传的文件(或者改里面的内容,改成上传成功) } }, error: function (index, upload) { //请求异常回调 //配置上面显示重传按钮 } }); }); </script> </body> </html>
运行效果:
三. 基于dropzonejs文件上传
1. 相关地址
官网:https://www.dropzonejs.com/ (含文档和下载地址)
2. 各种样例
(1).单文件上传
maxFiles: 1 将该属性设置为1,只能选择一个文件上传。
另外:该控件默认会生成一个缩略图框,我们很多情况下不需要,可以采用下面的方式来解决。
previewsContainer: '#hid', //将缩略图存放到指定位置(然后将该位置隐藏,则不显示缩略图了)
(2).多文件上传
A.原理:这里的多文件上传只调用一次接口!!
B.核心配置:
uploadMultiple: true, //开启单次请求上传多个文件, 配合下面的parallelUploads适用
parallelUploads: 6, //并行允许上传文件的个数
maxFiles: 6, //一次性上传的文件数量上限
successmultiple和errormultiple //多文件成功回调 和 失败回调 (只调一次)
代码分享:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>样例1</title> <style> .c1 { width: 600px; height: 280px; border: 1px solid black; padding: 20px; float: left; } img { width: 150px; height: 150px; } </style> <script src="../../../js/easyui/jquery.min.js" type="text/javascript" charset="utf-8"></script> <script src="../../../js/utils/dropzone.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $(function() { // 单文件上传 $("#test1").dropzone({ url: "http://localhost:29793/Demo_Areas/Demo1/SingleSaveFile", method: 'post', createImageThumbnails: false, //前端不生成缩略图(只是不生成图,但缩略框还在) headers: { "auth": window.localStorage.getItem("token") }, timeout: 1000000, //超时时间,单位毫秒, 默认30s maxFiles: 1, //一次性上传的文件数量上限(选择的时候最多选1个文件,无法多选) maxFilesize: 10, //最大文件上传大小 单位: MB previewsContainer: '#hid', //将缩略图存放到指定位置(将该位置隐藏,则不显示缩略图了) acceptedFiles: "image/*,application/pdf,.psd,.txt", //上传的类型 dictMaxFilesExceeded: "您最多只能一次上传n个文件", //替换文件数量超限的限制 dictInvalidFileType: '不支持该文件类型上传', //替换不支持类型上传的文案 dictFileTooBig: '您上传的文件太大,最大允许10M', //替换文件太大的文案 dictFallbackMessage: '您的浏览器不支持该上传控件', //替换浏览器不支持的文案 init:function(){ var that=this; this.on("complete",function(file){ // that.removeFile(file); that.removeAllFiles(); //执行完毕后,删除本地记录,使其可以继续上传(类似重置控件) }); }, sending: function(x1, x2, x3) { //发送文件之前调用,参数详见文档 }, success: function(file, res, e) { if (res.status == "ok") { var myUrl = "http://localhost:29793" + res.data; $("#j1").append('<img src="' + myUrl + '"/>'); } }, error: function(x1, errorMsg, xhr) { if (x1.status == "error") { alert(errorMsg); } } }); //多文件上传 $("#test2").dropzone({ url: "http://localhost:29793/Demo_Areas/Demo1/ManySaveFile", method: 'post', createImageThumbnails: false, //前端不生成缩略图(只是不生成图,但缩略框还在) headers: { "auth": window.localStorage.getItem("token") }, timeout: 1000000, //超时时间,单位毫秒, 默认30s uploadMultiple: true, //开启单次请求上传多个文件, 配合下面的parallelUploads适用 parallelUploads: 6, //并行允许上传文件的个数 maxFiles: 3, //一次性上传的文件数量上限 maxFilesize: 10, //最大文件上传大小 单位: MB previewsContainer: '#hid', //将缩略图存放到指定位置(将该位置隐藏,则不显示缩略图了) acceptedFiles: "image/*,application/pdf,.psd,.txt", //上传的类型 dictMaxFilesExceeded: "您最多只能一次上传n个文件", //替换文件数量超限的限制 dictInvalidFileType: '不支持该文件类型上传', //替换不支持类型上传的文案 dictFileTooBig: '您上传的文件太大,最大允许10M', //替换文件太大的文案 dictFallbackMessage: '您的浏览器不支持该上传控件', //替换浏览器不支持的文案 init:function(){ var that=this; this.on("complete",function(file){ that.removeAllFiles(); //执行完毕后,删除本地记录,使其可以继续上传(类似重置控件) }); }, sendingmultiple: function(x1, x2, x3) { //发送文件之前调用,参数详见文档 }, //多文件成功回调, 不能适用success回调,success会触发多次 successmultiple: function(file, res, e) { console.log(res); if (res.status == "ok") { for (var i = 0; i < res.data.length; i++) { var myUrl = "http://localhost:29793" + res.data[i]; $("#j2").append('<img src="' + myUrl + '"/>'); } } }, errormultiple: function(x1, errorMsg, xhr) { if (x1.status == "error") { alert(errorMsg); } }, error: function(x1, errorMsg, xhr) { if (x1.status == "error") { alert(errorMsg); } } }); }); </script> </head> <body> <p>图片上传dropzone样例</p> <div class="c1"> <button type="button" class="layui-btn" id="test1"> 单文件上传(各种属性) </button> <br /> <div id="j1"> </div> </div> <div class="c1"> <button type="button" class="layui-btn" id="test2"> 多文件上传 </button> <br /> <div id="j2"> </div> </div> <div id="hid" style="display: none;"> 用来存放缩略图,但不显示,目前没有找到直接关闭缩略图的属性 </div> </body> </html>
运行截图:
3. 其它
支持队列上传、文件分块上传、拖拽、其它各种情况的回调等等。
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?