一. 接口設計
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 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。