• 第51章 Bootstrap-Fileinput深入理解


    已经上传文件的回显

        BootstrapFileinput插件可以用与已经上传到服务器端(图片)文件的回显,但是这并不是对回显操作的一个好的解决方案,对于对回显操作而言最好不要使用BootstrapFileinput插件,但是作为BootstrapFileinput插件的一项基本功能,本人在示例中实现了通过BootstrapFileinput插件实现了(图片)文件的回显。

    Bootstrap-Fileinput插件在提交组合上传中存在的Bug

        如果希望通过Bootstrap-Fileinput插件上传文件的同时,通过进度条显示文件的上传状,必须使用“button”标签才能实现例如:

      <button type="button" class="btn btn-primary btn-upload-3">提交</button>

    否则会出现MulFileModel model中的moudel参数值会为:null,例如使用“input”标签

    @*<input type="submit" value="Create" class="btn btn-primary btn-upload-3" />*@

        实际上是把button”标签与BootstrapFileinput插件“上传”标签(<a>标签,可隐藏)进行映射绑定,从而来实现BootstrapFileinput插件上传文件的同时,通过进度条显示文件的上传状态,但<a>标签只能与“button”标签进行映射绑定,而不能与“input”标签进行映射绑定。

    多文件上传

        多文件上传的主要应用场景是:一次性对大批量的小文件上传并持久化保存到服务器端的磁盘中。

    .Net(Core)框架默认内置限制与配置

        在.Net(Core)框架中,客户端浏览器默认只能对服务器端“wwwroot”文件夹中的图片文件,进行显示,其它文件夹中的图片文件将不会被显示出来,如果客户端浏览器要显示其它文件夹中的图片文件,必须对.Net(Core)框架进行如下的配置定义:

    //表示允许访问默认“wwwroot”文件夹中的静态资源(这里包含图片、css和js文件)。

    app.UseStaticFiles();

    //如果不设定配置,则当前程序的页面中只能显示默认“wwwroot”文件夹中的静态文件(这里主要指图片)。

    app.UseStaticFiles(new StaticFileOptions

    {

        //资源所在的绝对路径。

        //当前程序根根目录中必须先新建“Images”,否则就会出现逻辑异常:

        //“System.IO.DirectoryNotFoundException:“C:\Users\Administrator\Desktop\BootstrapFileinputDemo\BootstrapFileinputDemo\Images\””

        FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "Images")),

        //表示访问路径,必须'/'开头

        RequestPath = "/Images"

    });

    提交组合多文件上传

    前台

    @model BootstrapFileinput.Models.MulFileModel

    @{

        ViewData["Title"] = "CombineSubmit_3";

    }

    @section styles {

        <link rel="stylesheet" href="~/bootstrap-icons/font/bootstrap-icons.min.css">

        <link rel="stylesheet" type="text/css" href="~/bootstrap-fileinput/css/fileinput.css">

        <link rel="stylesheet" type="text/css" href="~/font-awesome/css/all.css">

        <link rel="stylesheet" type="text/css" href="~/bootstrap-fileinput/themes/explorer-fas/theme.css">

         <style>

            #Time{

            font-size: 30px;

            /*text-align: center;*/

            }

        </style>

    }

    <div class="row">

        <div class="col-md-12">

            <form asp-action="CombineSubmit_3" enctype="multipart/form-data" method="post">

                <div asp-validation-summary="ModelOnly" class="text-danger"></div>

                  <div class="form-group">

                     <div class="file-loading">

                           @*如果通过该控件使用异步上传操作且上传行为方法不是默认的“CombineSubmit_1”,则必须去除“data-upload-url="#" ”,否则异步上传操作且上传行为方法一定是“CombineSubmit_1”。*@

                         <input asp-for="FormFileList" type="file" class="file" multiple data-preview-file-type="any"  data-upload-url="#" data-theme="fas"/>

                    </div>

                    <span asp-validation-for="FormFileList" class="text-danger"></span>

                </div>

                <div class="form-group">

                    <label asp-for="AlbumModel.Name" class="control-label"></label>

                    <input asp-for="AlbumModel.Name" class="form-control" />

                    <span asp-validation-for="AlbumModel.Name" class="text-danger"></span>

                </div>

                <div class="form-group">

                    @*<input type="submit" value="Create" class="btn btn-primary btn-upload-3" />*@

                    <button type="button" class="btn btn-primary btn-upload-3">提交</button>

                </div>

            </form>

        </div>

    </div>

    多文件已经上传成功,<span id="Time" style="color:red;">5</span>秒后自动跳转。

    @section Scripts {

        @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}

         <script type="text/javascript" src="~/bootstrap-fileinput/js/plugins/piexif.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/js/plugins/sortable.js"></script>

       

        <script type="text/javascript" src="~/bootstrap-fileinput/js/fileinput.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/js/locales/zh.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/themes/fas/theme.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/themes/explorer-fas/theme.js"></script>

       <script type="text/javascript">

        function countDown(secs) {

            var jumpTo = document.getElementById("Time");

            jumpTo.innerHTML = secs;

            if (--secs > 0)

                setTimeout("countDown(" + secs + ")", 1000);

            else

               window.location.href = "/Home/Index"

        }

       //初始化:bootstrap-fileinput 插件

        $('#FormFileList').fileinput({

        language: 'zh', //设置本地化语言(简体中文)。

        //bootstrap-fileinput插件UI相关的初始化设定。

        showClose: false,//是否显示关闭按钮。

        showCaption: true,//是否显示标题(上传输入框控件,默认值:true)。

        showRemove: false,//是否显示删除按钮。

        showUpload: false,//是否显示上传按钮。

        dropZoneEnabled: false,//不支持拖拽(是否显示拖拽区域,默认值:true)。

        dropZoneTitle: "可以将图片拖放到这里",//拖拽区域显示文字(默认值:true)。

        layoutTemplates: {

            //actionDelete:'', //去除上传预览的缩略图中的删除图标。

            actionUpload: '',//去除上传预览缩略图中的上传图片。

            // actionZoom: '',   //去除上传预览缩略图详情的图标(默认值:有图标)。

            actionDownload: '', //去除上传预览缩略图中的下载图标。

        },

        //上传相关设定。

        required: true,

        //enctype: 'multipart/form-data',//多文件上传模式。

        allowedFileExtensions: ['jpeg', 'jpg', 'jpe', "gif", "png", "bmp"],//允许上传文件的类型。

        //uploadUrl: "/Home/UploadFile_2",  //文件上传的行为方法。

        //   uploadAsync:多文件上传时如果使用bootstrap-fileinput插件内置控件或方法,该参数实例为:false时,则会把列表中的所有文件的数据同时提交到后台行为方法的对应参数中;

        // 如果该参数实例为:true,列表中的每1个文件都向提交到后台行为方法的对应参数中提交1次数据(即有多少文件向后台方法提交多少次,默认值:true)。

        uploadAsync: false,

        maxFileSize: 10240,   //单个上传文件最大值(10MB,.NetCore的所有上传文件的最大默认值是:30MB=30720KB),单位为kb,如果为0表示不限制文件大小。

        maxFileCount: 50, //每次上传允许的最多文件数。

        uploadExtraData: function () {

            return {

                albumName: $('#AlbumModel_Name').val()

            }; //上传时额外附加参数

        },

        //回显已经上传的图片文件。

        //overwriteInitial: false,

        //initialPreviewAsData: true,

        //initialPreview: imageArray,

        //回显已经上传的图片文件的说明信息。

        //initialPreviewConfig: [

        //    {caption: "transport-1.jpg", size: 329892, width: "120px", url: "{$url}", key: 1},

        //    {caption: "transport-2.jpg", size: 872378, width: "120px", url: "{$url}", key: 2},

        //    {caption: "transport-3.jpg", size: 632762, width: "120px", url: "{$url}", key: 3}

        //]

        validateInitialCount: true

    })

        .on('filebatchselected', function(event, files) {//批量选择操作后回调函数。

            var _maxRequestSize = 30720;

            _allFileSize = 0;

            $.each(files, function(idx, item) {

                _allFileSize += (item.size / 1024);

            });

            _allFileSize = parseFloat(_allFileSize.toFixed(2));

            if (_allFileSize > _maxRequestSize) {

                var msg = "选择的文件总大小(" + (_allFileSize / 1024).toFixed(2) + "MB)超出限制(" + (_maxRequestSize / 1024).toFixed(2) + "MB)";

                //如果所选择的上传文件的总大小大于限定值,则弹出警告窗口,如果点击弹出警告窗口中的“确定”按钮,则删除已经选择的所有的上传文件。

                $('#FormFileList').fileinput('clear');

                alert(msg);

            };

            //$(this).fileinput("upload");//选择上传文件完成后,自动对文件进行上传操作。

        })

        .on("filebatchuploadsuccess", function(event, data, previewId, index)  {   

        

            countDown(5);        

        });

        $(".btn-upload-3").on("click", function() {

            $("#FormFileList").fileinput('upload');

        });

    </script>

    }

    后台

          public IActionResult CombineSubmit_3()

            {

                return View();

            }

            [HttpPost]

            public IActionResult CombineSubmit_3(MulFileModel model, string albumName)

            {

                if (model == null || model.FormFileList == null || model.FormFileList.Count <= 0)

                    return View(model);

                model.AlbumModel.Name = _directoryPath;

                if (string.IsNullOrEmpty(albumName))

                {

                    if (model.AlbumModel.Id.ToString().Equals("00000000-0000-0000-0000-000000000000"))

                    {

                        model.AlbumModel.Id = Guid.NewGuid();

                    }

                    albumName= model.AlbumModel.Id.ToString();

                }

                model.AlbumModel.Name = albumName;

                string _imageDirectory = Path.Combine(_hostEnvironment.ContentRootPath, $"Images/{model.AlbumModel.Name}");

                if (!Directory.Exists(_imageDirectory))

                    Directory.CreateDirectory(_imageDirectory);

                foreach (var item in model.FormFileList)

                {

                    try

                    {

                        string _imagePath = Path.Combine(_imageDirectory, item.FileName);

                        FileStream _fileStream = new FileStream(_imagePath, FileMode.Create);

                        Stream _stream = item.OpenReadStream();

                        byte[] _buffer = new byte[_stream.Length];

                        _stream.Read(_buffer, 0, _buffer.Length);

                        _fileStream.Write(_buffer, 0, _buffer.Length);

                        _fileStream.Dispose();

                        model.UrlList.Add($"../Images/{model.AlbumModel.Name}/{item.FileName}");

                    }

                    catch (Exception ex)

                    {

                        //如果上传过程中产生错误,则删除指定文件夹中所有已经上传的文件。

                        string[] _updatedFileArray = Directory.GetFiles(_imageDirectory);

                        if (_updatedFileArray.Length > 0)

                        {

                            foreach (var filePath in _updatedFileArray)

                            {

                                System.IO.File.Delete(filePath);

                            }

                        }

                        ModelState.AddModelError("", ex.Message);

                        return View(model);

                    }

                }

                return Json(true);

            }

    单文件上传

    单文件上传的主要应用场景是:一次性对1个大文件上传并持久化保存到服务器端的磁盘中,单个大文件的上传操作有3种实现方式:

    1. 通过IFormFile类型的实例以内存缓冲的形式来把客户端中的文件从客户端内存中,全部缓冲存储到服务器端的内存中,然后把服务器端内存中的全部数据持久化存储到服务器端的磁盘中,然而这种方式会由于各种因素容易造成网站程序的崩溃,这种方式最好用于上传低频率的小文件,例如用户的头像图片。
    2. 通过调用“await reader.ReadNextSectionAsync()”方法,以“流式”,把客户端中的文件,分为成片,把1小片文件,上传的服务器端内存,并持久化到服务器端磁盘中后,再对下1片文件进行同样的操作,“流式”的分片操作有隐式和显式两种,隐式不显式设置每1片内存片的大小,这种方式最好用于单个大文件的上传操作(后台实现分片),当前1个大文件的所有文件片都已经上传到服务器端磁盘中后,服务器端将以顺序方式把这些文件片,重新整合为1个指定的大文件并服务器端磁盘中。

    3、通过Jquery方法直接把在前台客户端中就把客户端中的大文件直接分成指定的片,然后的操作与2中的操作本相同(前台实现分片)。

    .Net(Core)框架默认内置限制与配置

         注意1:在.Net(Core)框架通过内存缓冲的形式上传操作中,框架默认限制上传文件的大小,如果.Net(Core)框架想要上传大文件则首先必须对.Net(Core)框架定义以上配置:

        //把.NetCore框架中关于表单中文件上传最大限制值,由默认值:30000000B=29268.29268292683KB=28.6102294921875MB,设定为: long.MaxValue,并注入到内置依赖注入容器中。

    builder.Services

       .Configure<FormOptions>(x => {

           //单个窗体值的长度限制(表单的键值对中的值的长度限制)。 如果窗体包含的值超过此限制,则会 InvalidDataException 在分析时引发。 默认值为4194304字节,即约4MB。

           x.ValueLengthLimit = int.MaxValue;

           //每个多部分正文的长度限制(当表单enctype为multipart/form-data时文件的长度限制)。 超过此限制的窗体节将 InvalidDataException 在分析时引发。 默认值为134217728字节,即约128MB。

           //注意:MultipartBodyLengthLimit的类型为“long”,当然MultipartBodyLengthLimit也可以被设置为“int.MaxValue”,

           //但会造成“System.IO.InvalidDataException:“Multipart body length limit 2147483647(大概2GB) exceeded.””逻辑异常,所以该属性值最好被设置为“long.MaxValue” 。

           x.MultipartBodyLengthLimit = long.MaxValue;

           //(multipart头长度的限制,也就是boundary=--------Gefsgeq!34这种玩意儿的限制)。

           x.MultipartHeadersLengthLimit = int.MaxValue;

           //x.ValueLengthLimit = int.MaxValue;

           //x.ValueCountLimit = int.MaxValue;

           //x.MultipartBodyLengthLimit = long.MaxValue;

           //x.MultipartBoundaryLengthLimit = int.MaxValue;

           //x.BufferBodyLengthLimit = long.MaxValue;

           //x.BufferBody = true;

           //x.MemoryBufferThreshold = int.MaxValue;

           //x.KeyLengthLimit = int.MaxValue;

           //x.MultipartHeadersLengthLimit = int.MaxValue;

           //x.MultipartHeadersCountLimit = int.MaxValue;

       });

    内存缓冲式上传单个大文件

    前台

    @model BootstrapFileinput.Models.SingleFileModel

    @{

        ViewData["Title"] = "FormFileBig";

    }

    @section styles {

        <link rel="stylesheet" href="~/bootstrap-icons/font/bootstrap-icons.min.css">

        <link rel="stylesheet" type="text/css" href="~/bootstrap-fileinput/css/fileinput.css">

        <link rel="stylesheet" type="text/css" href="~/font-awesome/css/all.css">

        <link rel="stylesheet" type="text/css" href="~/bootstrap-fileinput/themes/explorer-fas/theme.css">

         <style>

            #Time{

            font-size: 30px;

            /*text-align: center;*/

            }

        </style>

    }

    <h5 class="alert alert-warning">已经测试:“zh-cn_windows_10_business_editions_version_21h2_x64_dvd_93b4cb1a.iso”,“5.34 GB (5,741,860,864 字节)”</h5>

    <div class="row">

        <div class="col-md-12">

            <form id="perForm" asp-action="FormFileBig" enctype="multipart/form-data"  method="post">

                <div asp-validation-summary="ModelOnly" class="text-danger"></div>

                <div class="form-group">

                     <div class="file-loading">

                           @*如果通过该控件使用异步上传操作且上传行为方法不是默认的“FormFileBig”,则必须去除“data-upload-url="#" ”,否则异步上传操作且上传行为方法一定是“FormFileBig”。*@

                         <input asp-for="FormFile" type="file" class="file" data-show-preview="false" data-theme="fas"/>

                    </div>

                    <span asp-validation-for="FormFile" class="text-danger"></span>

                </div>

                <div class="form-group">

                    <label asp-for="AlbumModel.Name" class="control-label"></label>

                    <input asp-for="AlbumModel.Name" class="form-control" />

                    <span asp-validation-for="AlbumModel.Name" class="text-danger"></span>

                </div>

                <div class="form-group">

                    @*<input type="submit" value="提交" class="btn btn-primary btn-upload-3"/>*@

                    <button type="button" class="btn btn-primary btn-upload-3">提交</button>

                </div>

            </form>

        </div>

    </div>

    单个大文件已经上传成功,<span id="Time" style="color:red;">5</span>秒后自动跳转。

    @section Scripts {

        @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}

        <script type="text/javascript" src="~/bootstrap-fileinput/js/plugins/piexif.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/js/plugins/sortable.js"></script>

       

        <script type="text/javascript" src="~/bootstrap-fileinput/js/fileinput.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/js/locales/zh.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/themes/fas/theme.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/themes/explorer-fas/theme.js"></script>

        <script type="text/javascript">

        function countDown(secs) {

            var jumpTo = document.getElementById("Time");

            jumpTo.innerHTML = secs;

            if (--secs > 0)

                setTimeout("countDown(" + secs + ")", 1000);

            else

               window.location.href = "/Home/Index"

        }

       //初始化:bootstrap-fileinput 插件

        $('#FormFile').fileinput({

        language: 'zh', //设置本地化语言(简体中文)。

        //bootstrap-fileinput插件UI相关的初始化设定。

        showClose: false,//是否显示关闭按钮。

        showCaption: true,//是否显示标题(上传输入框控件,默认值:true)。

        showRemove: false,//是否显示删除按钮。

        showUpload: false,//是否显示上传按钮。

        dropZoneEnabled: false,//不支持拖拽(是否显示拖拽区域,默认值:true)。

        dropZoneTitle: "可以将图片拖放到这里",//拖拽区域显示文字(默认值:true)。

        layoutTemplates: {

            //actionDelete:'', //去除上传预览的缩略图中的删除图标。

            actionUpload: '',//去除上传预览缩略图中的上传图片。

            // actionZoom: '',   //去除上传预览缩略图详情的图标(默认值:有图标)。

            actionDownload: '', //去除上传预览缩略图中的下载图标。

        },

        //上传相关设定。

        required: true,

        //enctype: 'multipart/form-data',//多文件上传模式。

        allowedFileExtensions: ['rar', 'zip','iso', 'mp4'],//允许上传文件的类型。

        uploadUrl: "/Home/UploadFileFormFileBig"//文件上传的行为方法。

        //   uploadAsync:多文件上传时如果使用bootstrap-fileinput插件内置控件或方法,该参数实例为:false时,则会把列表中的所有文件的数据同时提交到后台行为方法的对应参数中;

        // 如果该参数实例为:true,列表中的每1个文件都向提交到后台行为方法的对应参数中提交1次数据(即有多少文件向后台方法提交多少次,默认值:true)。

        uploadAsync: false,

        //maxFileSize: 10240,   //单个上传文件最大值(10MB,.NetCore的所有上传文件的最大默认值是:30MB=30720KB),单位为kb,如果为0表示不限制文件大小。

        maxFileCount: 1, //每次上传允许的最多文件数。

        //回显已经上传的图片文件。

        //overwriteInitial: false,

        //initialPreviewAsData: true,

        //initialPreview: imageArray,

        //回显已经上传的图片文件的说明信息。

        //initialPreviewConfig: [

        //    {caption: "transport-1.jpg", size: 329892, width: "120px", url: "{$url}", key: 1},

        //    {caption: "transport-2.jpg", size: 872378, width: "120px", url: "{$url}", key: 2},

        //    {caption: "transport-3.jpg", size: 632762, width: "120px", url: "{$url}", key: 3}

        //]

        validateInitialCount: true

    })

        .on("filebatchuploadsuccess", function(event, data, previewId, index)  {   

             var formData = new FormData($('#perForm')[0]);

            $.ajax({

                 type : 'post',

                 dataType : "json",

                 cache : false,//上传文件不需要缓存

                 data : formData,

                 processData : false,//data值是FormData对象,不需要对数据进行处理

                 contentType : false,//contentType值,因为是由<form>表单构造的FormData对象,且已经声明了属性enctype="multipart/form-data",所以这里设置为false。

                 beforeSend: function(xhr){

                   

                },

                success : function(data) {

                     

                 }

             });

            countDown(5);        

        });

        $(".btn-upload-3").on("click", function() {

            $("#FormFile").fileinput('upload');

        });

    </script>

    }

    后台

        注意2:内存缓冲式上传单个大文件必须在上传方法上定义标记:[DisableRequestSizeLimit]

            public IActionResult FormFileBig()

            {

                return View();

            }

           

            [HttpPost]

            public  IActionResult FormFileBig(SingleFileModel model)

            {

                if (!string.IsNullOrEmpty(model.AlbumModel.Name))

                {

                    string _sourceDirectory = Path.Combine(_hostEnvironment.ContentRootPath, $"Images/{_singleFileModel.AlbumModel.Name}");

                    string _imageDirectory = Path.Combine(_hostEnvironment.ContentRootPath, $"Images/{model.AlbumModel.Name}");

                    if (!Directory.Exists(_imageDirectory))

                    {

                        Directory.Move(_sourceDirectory, _imageDirectory);

                        _singleFileModel.Url = string.Empty;

                        _singleFileModel.Url = Directory.GetFiles(_imageDirectory).FirstOrDefault();

                    }

                }

                return View(model);

            }

            [DisableRequestSizeLimit]

            //[RequestSizeLimit(100_000_000)] //或最大限制100MB左右

            public async Task<IActionResult> UploadFileFormFileBig(SingleFileModel model)

            {

                if (model == null || model.FormFile == null || model.FormFile.Length <= 0)

                    return View(model);

                model.AlbumModel.Name = _directoryPath;

                if (string.IsNullOrEmpty(model.AlbumModel.Name))

                {

                    if (model.AlbumModel.Id.ToString().Equals("00000000-0000-0000-0000-000000000000"))

                    {

                        model.AlbumModel.Id = Guid.NewGuid();

                    }

                    model.AlbumModel.Name = model.AlbumModel.Id.ToString();

                }

                string _imageDirectory = Path.Combine(_hostEnvironment.ContentRootPath, $"Images/{model.AlbumModel.Name}");

                if (!Directory.Exists(_imageDirectory))

                    Directory.CreateDirectory(_imageDirectory);

                string _filePath = Path.Combine(_imageDirectory, model.FormFile.FileName);

                using (var stream = model.FormFile.OpenReadStream())

                {

                    await WriteFileAsync(stream, _filePath);

                    model.Url = $"../Images/{model.AlbumModel.Name}/{model.FormFile.Name}";

                }

                _singleFileModel = model;

                return Json(true);

            }

            public static async Task<int> WriteFileAsync(Stream stream, string filePath)

           {

                const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小。

                int _writeCount = 0;

                using(FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true))

                {

                    byte[] _byteArray = new byte[FILE_WRITE_SIZE];

                    int _readConunt = 0;

                    while((_readConunt = await stream.ReadAsync(_byteArray,0,_byteArray.Length)) > 0)

                    {

                        await fileStream.WriteAsync(_byteArray,0, _readConunt);

                        _writeCount += _readConunt;

                    }

                }

                return _writeCount;

            }

    Bootstrap-Fileinput流式分片(断点)上传单个大文件

    前台

    @model BootstrapFileinput.Models.SingleFileModel

    @{

        ViewData["Title"] = "BootstrapFileinputBig";

    }

    @section styles {

        <link rel="stylesheet" href="~/bootstrap-icons/font/bootstrap-icons.min.css">

        <link rel="stylesheet" type="text/css" href="~/bootstrap-fileinput/css/fileinput.css">

        <link rel="stylesheet" type="text/css" href="~/font-awesome/css/all.css">

        <link rel="stylesheet" type="text/css" href="~/bootstrap-fileinput/themes/explorer-fas/theme.css">

         <style>

            #Time{

            font-size: 30px;

            }

        </style>

    }

    <h5 class="alert alert-warning">已经测试:“zh-cn_windows_10_business_editions_version_21h2_x64_dvd_93b4cb1a.iso”,“5.34 GB (5,741,860,864 字节)”</h5>

    <div class="row">

        <div class="col-md-12">

            <form id="perForm" asp-action="BootstrapFileinputBig"  enctype="multipart/form-data" method="post">

                <div asp-validation-summary="ModelOnly" class="text-danger"></div>

                <div class="form-group">

                     <div class="file-loading">

                           @*如果通过该控件使用异步上传操作且上传行为方法不是默认的“BootstrapFileinputBig”,则必须去除“data-upload-url="#" ”,否则异步上传操作且上传行为方法一定是“BootstrapFileinputBig”。*@

                         <input asp-for="FormFile" type="file" class="file" data-show-preview="false" data-upload-url="#" data-theme="fas"/>

                    </div>

                    <span asp-validation-for="FormFile" class="text-danger"></span>

                </div>

                 <div class="form-group">

                    <label asp-for="AlbumModel.Name" class="control-label"></label>

                    <input asp-for="AlbumModel.Name" class="form-control" />

                    <span asp-validation-for="AlbumModel.Name" class="text-danger"></span>

                </div>

                <div class="form-group">

                    <button type="button" class="btn btn-primary btn-upload-3">提交</button>

                </div>

            </form>

        </div>

    </div>

    单个大文件已经上传成功,<span id="Time" style="color:red;">5</span>秒后自动跳转。

    @section Scripts {

        @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}

         <script type="text/javascript" src="~/bootstrap-fileinput/js/plugins/piexif.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/js/plugins/sortable.js"></script>

       

        <script type="text/javascript" src="~/bootstrap-fileinput/js/fileinput.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/js/locales/zh.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/themes/fas/theme.js"></script>

        <script type="text/javascript" src="~/bootstrap-fileinput/themes/explorer-fas/theme.js"></script>

        <script type="text/javascript">

             $("#FormFile").fileinput({

                 

                //bootstrap-fileinput插件UI相关的初始化设定。

                language: 'zh', //设置本地化语言(简体中文)。

                showClose: false,//是否显示关闭按钮。

                showCaption: true,//是否显示标题(上传输入框控件,默认值:true)。

                showRemove: false,//是否显示删除按钮。

                showUpload: false,//是否显示上传按钮。

                 //上传相关设定。

                required: true,

                //enctype: 'multipart/form-data',//多文件上传模式。

                allowedFileExtensions: ['rar', 'zip','iso', 'mp4'],//允许上传文件的类型。

                //   uploadAsync:多文件上传时如果使用bootstrap-fileinput插件内置控件或方法,该参数实例为:false时,则会把列表中的所有文件的数据同时提交到后台行为方法的对应参数中;

                //如果该参数实例为:true,列表中的每1个文件都向提交到后台行为方法的对应参数中提交1次数据(即有多少文件向后台方法提交多少次,默认值:true)。

                //uploadAsync: false,

                //uploadUrl: "/Home/BootstrapFileinputBig",

                //maxFileSize: 10240,   //单个上传文件最大值(10MB,.NetCore的所有上传文件的最大默认值是:30MB=30720KB),单位为kb,如果为0表示不限制文件大小。

                maxFileCount: 1, //每次上传允许的最多文件数。

                 //单个大文件分片上传上传相关设定。

                enableResumableUpload: true,

                resumableUploadOptions: {

                    chunkSize: 8*1024, //单个大文件分片上传中每1分片的大小:8 MB。

                },

              uploadExtraData: function () {

                    return {

                        albumName: $('#AlbumModel_Name').val()

                    }; //上传时额外附加参数

               },

              //回显已经上传的图片文件。

              //overwriteInitial: false,

              //initialPreviewAsData: true,

              //initialPreview: imageArray,

              //回显已经上传的图片文件的说明信息。

              //initialPreviewConfig: [

              //    {caption: "transport-1.jpg", size: 329892, width: "120px", url: "{$url}", key: 1},

              //    {caption: "transport-2.jpg", size: 872378, width: "120px", url: "{$url}", key: 2},

              //    {caption: "transport-3.jpg", size: 632762, width: "120px", url: "{$url}", key: 3}

              //]

               validateInitialCount: true,

        

        })

        .on('fileuploaded', function(event, previewId, index, fileId) {

            console.log('File Uploaded', 'ID: ' + fileId + ', Thumb ID: ' + previewId);

        }).on('fileuploaderror', function(event, data, msg) {

            console.log('File Upload Error', 'ID: ' + data.fileId + ', Thumb ID: ' + data.previewId);

        }).on('filebatchuploadcomplete', function(event, preview, config, tags, extraData) {

           // console.log('File Batch Uploaded', preview, config, tags, extraData);

            countDown(5);        

        });

       

        $(".btn-upload-3").on("click", function() {

            $("#FormFile").fileinput('upload');

              

        });

          function countDown(secs) {

            var jumpTo = document.getElementById("Time");

            jumpTo.innerHTML = secs;

            if (--secs > 0)

                setTimeout("countDown(" + secs + ")", 1000);

            else

               window.location.href = "/Home/Index"

        }

        </script>

    }

    后台

            public IActionResult BootstrapFileinputBig()

            {

                return View();

            }

            [HttpPost]

            public async Task<IActionResult> BootstrapFileinputBig(SingleFileModel model, string albumName)

            {

                IFormFile _formFile = Request.Form.Files[0];

                string _fileName = Request.Form["fileName"];

                long _chunkIndex = Convert.ToInt64(Request.Form["chunkIndex"]);

                long _chunkSize = Convert.ToInt64(Request.Form["chunkSize"]);

                long _chunkCount = Convert.ToInt64(Request.Form["chunkCount"]);

                long _fileSize = Convert.ToInt64(Request.Form["fileSize"]);

                Dictionary<string, object> result = new Dictionary<string, object>();

                if (string.IsNullOrEmpty(_directoryPath))

                {

                    if(!string.IsNullOrEmpty(albumName))

                    {

                        _directoryPath = Path.Combine(_environment.ContentRootPath, "Images", albumName);

                    }

                    else

                    {

                        _directoryPath = Path.Combine(_environment.ContentRootPath, "Images", Guid.NewGuid().ToString());

                    }

                }

                   

                if (!Directory.Exists(_directoryPath))

                    Directory.CreateDirectory(_directoryPath);

                try

                {

                    string _targetFile = _chunkIndex.ToString() + "_" + _fileName;

                    _targetFile = Path.Combine(_directoryPath, _targetFile);

                    if (!Convert.IsDBNull(_formFile))

                    {

                        await Task.Run(() =>

                        {

                            FileStream fs = new FileStream(_targetFile, FileMode.Create);

                            _formFile.CopyTo(fs);

                            fs.Close();

                        });

                    }

                }

                catch (Exception ex)

                {

                    Directory.Delete(_directoryPath);//删除文件夹

                    result.Add("error", "分片文件:" + _chunkIndex + "上传过程中产生错误。逻辑异常信息:" + ex);

                    return Json(result);

                }

                if (_chunkCount == _chunkIndex + 1)

                {

                    string mergefileNamePath = Path.Combine(_environment.ContentRootPath, "Images", _fileName);

                    await FileMerge(_directoryPath, mergefileNamePath, _fileSize);

                }

                result.Add("chunkIndex", _chunkIndex);

                result.Add("append", true);

                return Json(result);

            }

            public async Task FileMerge(string directoryPath, string fileName, long fileSize)

            {

                long _serverFilesLength = 0;

                GetDirSizeByPath(directoryPath, ref _serverFilesLength);

                //如果客户端正在上传文件的大小等于所有分片文件的大小,则把所有的分片文件组合为1个指定的文件持久化存储到服务器端的磁盘中。

                if (fileSize.Equals(_serverFilesLength))

                {

                    try

                    {

                        var files = Directory.GetFiles(directoryPath);//获得下面的所有文件

                        var fs = new FileStream(fileName, FileMode.Create);

                        foreach (var part in files.OrderBy(x => x.Length).ThenBy(x => x))//排一下序,保证从0-N Write

                        {

                            var bytes = System.IO.File.ReadAllBytes(part);

                            await fs.WriteAsync(bytes, 0, bytes.Length);

                            bytes = null;

                        }

                        fs.Close();

                        ThoroughDeleteDirectory(directoryPath);

                        _directoryPath = string.Empty;

                    }

                    catch (Exception ex)

                    {

                        throw ex;

                    }

                }

            }

            public static void ThoroughDeleteDirectory(string directoryPath)

            {

                foreach (var path in Directory.GetFileSystemEntries(directoryPath))

                {

                    if (System.IO.File.Exists(path))

                    {

                        // 删除文件夹

                        System.IO.File.Delete(path);

                    }

                    else

                    {

                        // 递归删除文件夹

                        ThoroughDeleteDirectory(path);

                    }

                }

                // 删除空文件夹

                Directory.Delete(directoryPath);

            }

            /// <summary>

            /// 获取某一文件夹的大小

            /// </summary>

            /// <param name="dir">文件夹目录</param>

            /// <param name="dirSize">文件夹大小</param>

            public static void GetDirSizeByPath(string dir, ref long dirSize)

            {

                try

                {

                    DirectoryInfo dirInfo = new DirectoryInfo(dir);

                    DirectoryInfo[] dirs = dirInfo.GetDirectories();

                    FileInfo[] files = dirInfo.GetFiles();

                    foreach (var item in dirs)

                    {

                        GetDirSizeByPath(item.FullName, ref dirSize);

                    }

                    foreach (var item in files)

                    {

                        dirSize += item.Length;

                    }

                }

                catch (Exception ex)

                {

                    Console.WriteLine("获取文件大小失败" + ex.Message);

                }

            }

    总结:

        bootstrap-fileinput插件主要用于对文件上传过程中的UI展示,以提升用户体验,这种第3方插件有很多,例如:“webuploader”, “webuploader”插件是网络上有许多示例,比较容易上手,而bootstrap-fileinput则惨的多,特别是对于.Net(Core)框架的完整示例1个也没有,但是“webuploader”插件也有1个致命的缺点,它是通过flash动画文件来展示文件上传的动态进度,这已经与当前HTML的发展方向背道而驰,而bootstrap-fileinput插件就不存在这个问题,我经过多方法的权衡,最终选择了bootstrap-fileinput插件作为UI上传展示插件,由于本人对Jquery并不擅长和示例的稀缺,本人花了1个月的时间完成了bootstrap-fileinput插件在.Net(Core)框架的完整示例,这个成本对于我来说有点大,其中也不乏放弃的相法,但是考虑到以后坚持了下来,最终把.Net(Core)框架中关于bootstrap-fileinput插件的各种应用场景都作了相关的示例,但还有1个场景的示例本人没有提供:断点续传,如果本人有时间会把该示例补上,如果有看过本章的开发者,实现了该示例,请在评论区通知我。

    对以上功能更为具体实现和注释见:22-06-23-051_BootstrapFileinput(文件上传深入理解示例)。

  • 相关阅读:
    人工智能提示(prompt)工程入门
    Matlab 多项式插值(曲线拟合)
    mysql交互下的命令
    第3章 Spring Boot进阶,开发社区核心功能(上)
    HTTP请求和响应(补充HTTP协议)
    html+css 带图片的搜索框
    8、Feign远程调用
    为什么企业不愿意升级ERP系统
    Docker 离线安装(2022/11/08)
    vue-cli中总提示组件没有正确注册
  • 原文地址:https://blog.csdn.net/zhoujian_911/article/details/125425153