Home  >  Article  >  Backend Development  >  Detailed explanation of the example of uploading multiple files using .NetCore

Detailed explanation of the example of uploading multiple files using .NetCore

Y2J
Y2JOriginal
2017-04-19 16:36:252530browse

This chapter shares with you an example of uploading files using .NetCore's MVC framework. The main contents include: form submission for upload, ajax upload, ajax submission + upload progress effect, Task parallel processing + ajax submission + upload progress, It is very helpful to everyone. If you are interested, please follow the editor to learn

This chapter shares with you an example of uploading files using the MVC framework of .NetCore. The main contents are: form submission and upload, ajax Upload, ajax submission + upload progress effect, Task parallel processing + ajax submission + upload progress. I believe you will get a good harvest after reading the content of the article. If you can, you might as well give it a like; because the computer ran out of power yesterday, I will almost finish writing it. The content was not saved, so I came to the company early this morning and started over from scratch. The power outage was indeed a headache, but for the sake of the community’s sharing environment, it was worth it. Without further ado, let’s get into today’s main part;

Upload a set of pictures in form

Let’s take a look at our html code first. Here is a brief explanation of what is required to upload files. Set the enctype="multipart/form-data" attribute and post method in the form element. If you want to upload multiple files, you need to set the file type='file' element to her attribute multiple='multiple', so With the following content:


<form class="form-horizontal" action="/Home/FileUp" method="post" enctype="multipart/form-data">
  <input type="file" name="MyPhoto" class="form-control" multiple />
  <br />
  <button class="btn btn-default">form上传</button>
  <br />
  <span style="color:red">@ViewData["MsgBox"]</span>
  </form>

Due to the use of form submission, this test case only uses the default type=submit of the button element to submit the form, and the corresponding code in the background Action As follows:


/// <summary>
 /// form提交上传
 /// </summary>
 /// <param name="user"></param>
 /// <returns></returns>
 [HttpPost]
 public async Task<IActionResult> FileUp(MoUser user)
 {
  if (user.MyPhoto == null || user.MyPhoto.Count <= 0) { MsgBox("请上传图片。"); return View(); }
  //var file = Request.Form.Files;
  foreach (var file in user.MyPhoto)
  {
  var fileName = file.FileName;
  var contentType = file.ContentType;
  var len = file.Length;
  var fileType = new string[] { "image/jpeg", "image/png" };
  if (!fileType.Any(b => b.Contains(contentType))) { MsgBox($"只能上传{string.Join(",", fileType)}格式的图片。"); return View(); }
  if (len > 1024 * 1024 * 4) { MsgBox("上传图片大小只能在4M以下。"); return View(); }
  var path = Path.Combine(@"D:\F\学习\vs2017\netcore\netcore01\WebApp01\wwwroot\myfile", fileName);
  using (var stream = System.IO.File.Create(path))
  {
   await file.CopyToAsync(stream);
  }
  }
  MsgBox($"上传成功");
  return View();
 }

The Action from the front end to the back end has to be said that this form submission method is quite simple. It should be noted that the entity model method used in the Action here is Corresponding to the uploaded file information, the MoUser class is customized here, and the name of the file type='file' in the html form is matched through the attribute public Listbd57100a971f76ffcb06f3d1bfb26aea MyPhoto { get; set; } ##Attribute namename="MyPhoto":


public class MoUser
 {
 public int UserId { get; set; } = 1;
 public string UserName { get; set; } = "神牛步行3";
 public List<IFormFile> MyPhoto { get; set; }
 }

In this way, the uploaded file information can be stored in the custom MoUser through the entity model The MyPhoto attribute in the class is included;

ajax uploads a set of pictures

Here you need to modify some things in the html in the above example. Instead of using form submission, a normal button button is specified to trigger ajax submission. The complete html code is as follows:


<form class="form-horizontal" id="form01" method="post" enctype="multipart/form-data">
  <input type="file" name="MyPhoto01" class="form-control" multiple />
  <br />
  <button type="button" id="btnAjax" class="btn btn-default">ajax上传</button>
  <br />
  <span style="color:red" id="span01"></span>
  </form>

With the layout, let’s take a look at the specific js implementation code , here I use the ajax submission method of jquery to operate, and also use the new FormData of html5 to store the form data:

##

$("#btnAjax").on("click", function () {
  var msg = $("#span01");
  var form = document.getElementById("form01");
  //console.log(form);
  var data = new FormData(form);
  $.ajax({
  type: "POST",
  url: "/home/AjaxFileUp",
  data: data,
  contentType: false,
  processData: false,
  success: function (data) {
   if (data) {
   msg.html(data.msg);
   }
  },
  error: function () {
   msg.html("上传文件异常,请稍后重试!");
  }
  });
 });

As for the background Action methods and examples There is not much difference between one and the other. The key point is that here I directly use the Request.Form.Files method to obtain all the uploaded files, instead of using the entity model method. This way the test cases will be more diverse:

/// <summary>
 /// ajax无上传进度效果上传
 /// </summary>
 /// <returns></returns>
 [HttpPost]
 public async Task<JsonResult> AjaxFileUp()
 {
  var data = new MoData { Msg = "上传失败" };
  try
  {
  var files = Request.Form.Files.Where(b => b.Name == "MyPhoto01");
  //非空限制
  if (files == null || files.Count() <= 0) { data.Msg = "请选择上传的文件。"; return Json(data); }
  //格式限制
  var allowType = new string[] { "image/jpeg", "image/png" };
  if (files.Any(b => !allowType.Contains(b.ContentType)))
  {
   data.Msg = $"只能上传{string.Join(",", allowType)}格式的文件。";
   return Json(data);
  }
  //大小限制
  if (files.Sum(b => b.Length) >= 1024 * 1024 * 4)
  {
   data.Msg = "上传文件的总大小只能在4M以下。"; return Json(data);
  }
  //写入服务器磁盘
  foreach (var file in files)
  {
   var fileName = file.FileName;
   var path = Path.Combine(@"D:\F\学习\vs2017\netcore\netcore01\WebApp01\wwwroot\myfile", fileName);
   using (var stream = System.IO.File.Create(path))
   {
   await file.CopyToAsync(stream);
   }
  }
  data.Msg = "上传成功";
  data.Status = 2;
  }
  catch (Exception ex)
  {
  data.Msg = ex.Message;
  }
  return Json(data);
 }

If you have the patience to read this, then I personally feel that the following content will be very helpful to your development and will live up to your expectations;

ajax submission + upload progress + a set of image uploadsSimilarly, let’s look at the corresponding html code first. In fact, it is almost the same as example 2, except that the name has been changed:

<form class="form-horizontal" id="form02" method="post" enctype="multipart/form-data">
  <input type="file" name="MyPhoto02" class="form-control" multiple />
  <br />
  <button type="button" id="btnAjax02" class="btn btn-default">ajax上传进度效果上传</button>
  <br />
  <span style="color:red" id="span02"></span>
  </form>

To add a progress effect, you need to use a js timer to regularly obtain the upload progress data information of the uploaded file, so here you use the setInterval method of js to regularly request a progress data interface , please note that you need to clear this timer after use, otherwise it will keep requesting your interface:

 $("#btnAjax02").on("click", function () {
  var interBar;
  var msg = $("#span02");
  msg.html("上传中,请稍后...");
  var form = document.getElementById("form02");
  //console.log(form);
  var data = new FormData(form);
  $.ajax({
   type: "POST",
   url: "/home/AjaxFileUp02",
   data: data,
   contentType: false,
   processData: false,
   success: function (data) {
   if (data) {
    msg.html(data.msg);
    //清除进度查询
    if (interBar) { clearInterval(interBar); }
   }
   },
   error: function () {
   msg.html("上传文件异常,请稍后重试!");
   if (interBar) { clearInterval(interBar); }
   }
  });
  //获取进度
  interBar = setInterval(function () {
   $.post("/home/ProgresBar02", function (data) {
   if (data) {
    var isClearVal = true;
    var strArr = [];
    $.each(data, function (i, item) {
    strArr.push(&#39;文件:&#39; + item.fileName + ",当前上传:" + item.percentBar + &#39;<br/>&#39;);
    if (item.status != 2) { isClearVal = false; }
    });
    msg.html(strArr.join(&#39;&#39;));
    if (isClearVal) {
    if (interBar) { clearInterval(interBar); }
    }
   }
   });
  }, 200);
  });

Since the separate progress data interface is mentioned above, in addition to uploading Action In addition, progress Action is also needed, and the uploaded file data information obtained by this progress Action must be consistent with the uploaded Action, so it is necessary to use cache and other methods of storing data. Here I use the MemoryCache method, which is used by netcore. It is said that you only need to add the component service in the startup file (such as Startup.cs):

 public void ConfigureServices(IServiceCollection services)
  {
  // Add framework services.
  services.AddMvc();
 
  //添加cache支持
  services.AddDistributedMemoryCache();
 }

and then inject it into the corresponding interface Controller through the constructor:

  readonly IMemoryCache _cache;
 public HomeController(IOptions<MoOptions> options, ILogger<HomeController> logger, IMemoryCache cache)
 {  this._options = options.Value;
  _logger = logger;
  _cache = cache; 
 }

Now we can use the cache to store our upload progress information. Let’s take a look at the Action to process the upload:

private string cacheKey = "UserId_UpFile";
 private string cacheKey03 = "UserId_UpFile03";
 /// <summary>
 /// ajax上传进度效果上传
 /// </summary>
 /// <returns></returns>
 [HttpPost]
 public async Task<JsonResult> AjaxFileUp02()
 {
  var data = new MoData { Msg = "上传失败" };
  try
  {
  var files = Request.Form.Files.Where(b => b.Name == "MyPhoto02");
  //非空限制
  if (files == null || files.Count() <= 0) { data.Msg = "请选择上传的文件。"; return Json(data); }
  //格式限制
  var allowType = new string[] { "image/jpeg", "image/png" };
  if (files.Any(b => !allowType.Contains(b.ContentType)))
  {
   data.Msg = $"只能上传{string.Join(",", allowType)}格式的文件。";
   return Json(data);
  }
  //大小限制
  if (files.Sum(b => b.Length) >= 1024 * 1024 * 4)
  {
   data.Msg = "上传文件的总大小只能在4M以下。"; return Json(data);
  }
  //初始化上传多个文件的Bar,存储到缓存中,方便获取上传进度
  var listBar = new List<MoBar>();
  files.ToList().ForEach(b =>
  {
   listBar.Add(new MoBar
   {
   FileName = b.FileName,
   Status = 1,
   CurrBar = 0,
   TotalBar = b.Length
   });
  });
  _cache.Set<List<MoBar>>(cacheKey, listBar);
  //写入服务器磁盘
  foreach (var file in files)
  {
   //总大小
   var totalSize = file.Length;
   //初始化每次读取大小
   var readSize = 1024L;
   var bt = new byte[totalSize > readSize ? readSize : totalSize];
   //当前已经读取的大小
   var currentSize = 0L;
   var fileName = file.FileName;
   var path = Path.Combine(@"D:\F\学习\vs2017\netcore\netcore01\WebApp01\wwwroot\myfile", fileName);
   using (var stream = System.IO.File.Create(path))
   {
   //await file.CopyToAsync(stream);
   //进度条处理流程
   using (var inputStream = file.OpenReadStream())
   {
    //读取上传文件流
    while (await inputStream.ReadAsync(bt, 0, bt.Length) > 0)
    {
    //当前读取的长度
    currentSize += bt.Length;
    //写入上传流到服务器文件中
    await stream.WriteAsync(bt, 0, bt.Length);
    //获取每次读取的大小
    readSize = currentSize + readSize <= totalSize ?
     readSize :
     totalSize - currentSize;
    //重新设置
    bt = new byte[readSize];
    //设置当前上传的文件进度,并重新缓存到进度缓存中
    var bars = _cache.Get<List<MoBar>>(cacheKey);
    var currBar = bars.Where(b => b.FileName == fileName).SingleOrDefault();
    currBar.CurrBar = currentSize;
    currBar.Status = currentSize >= totalSize ? 2 : 1;
    _cache.Set<List<MoBar>>(cacheKey, bars);
    System.Threading.Thread.Sleep(1000 * 1);
    }
   }
   }
  }
  data.Msg = "上传完成";
  data.Status = 2;
  }
  catch (Exception ex)
  {
  data.Msg = ex.Message;
  }
  return Json(data);
 }

The code suddenly increased. In fact, logically speaking, it only adds a Cache to store the progress, and the logic of reading the uploaded file stream one by one. You can take a closer look at the code, and there are notes. Next, there are What is our progress information Action interface:

[HttpPost]
  public JsonResult ProgresBar02()
  {
   var bars = new List<MoBar>();
   try
   {
    bars = _cache.Get<List<MoBar>>(cacheKey);
   }
   catch (Exception ex)
   {
   }
   return Json(bars);
  }

The progress interface only needs to obtain the progress information in the cache. Note: This is a test case. Please refer to the specific usage scenarios by yourself. Add other logic code; let’s take a look at the screenshot of the effect:

Task parallel processing + ajax submission + upload progress + a set of image uploads

这一小节,将会使用Task来处理上传的文件,通过上一小节截图能够看出,如果你上传多个文件,那么都是按照次序一个一个读取文件流来生成上传文件到服务器,这里改良一下利用Task的特点,就能实现同时读取不同文件流了,先来看下html代码和js代码:


<form class="form-horizontal" id="form03" method="post" enctype="multipart/form-data">
    <input type="file" name="MyPhoto03" class="form-control" multiple />
    <br />
    <button type="button" id="btnAjax03" class="btn btn-default">task任务处理ajax上传进度效果上传</button>
    <br />
    <span style="color:red" id="span03"></span>
   </form>

由于和示例3的js代码无差别这里我直接贴出代码:


$("#btnAjax03").on("click", function () {
   var interBar;
   var msg = $("#span03");
   msg.html("上传中,请稍后...");
   var form = document.getElementById("form03");
   //console.log(form);
   var data = new FormData(form);
   $.ajax({
    type: "POST",
    url: "/home/AjaxFileUp03",
    data: data,
    contentType: false,
    processData: false,
    success: function (data) {
     if (data) {
      msg.html(data.msg);
      //清除进度查询
      if (interBar) { clearInterval(interBar); }
     }
    },
    error: function () {
     msg.html("上传文件异常,请稍后重试!");
     if (interBar) { clearInterval(interBar); }
    }
   });
   //获取进度
   interBar = setInterval(function () {
    $.post("/home/ProgresBar03", function (data) {
     if (data) {
      var isClearVal = true;
      var strArr = [];
      $.each(data, function (i, item) {
       strArr.push(&#39;文件:&#39; + item.fileName + ",当前上传:" + item.percentBar + &#39;<br/>&#39;);
       if (item.status != 2) { isClearVal = false; }
      });
      msg.html(strArr.join(&#39;&#39;));
      if (isClearVal) {
       if (interBar) { clearInterval(interBar); }
      }
     }
    });
   }, 200);
  });

关键点在后台,通过task数组来存储每个上传文件的处理任务 Task[] tasks = new Task[len]; ,然后使用 Task.WaitAll(tasks); 等待所有上传任务的完成,这里特别注意了这里必须等待,不然会丢失上传文件流(多次测试结果):


/// <summary>
  /// ajax上传进度效果上传
  /// </summary>
  /// <returns></returns>
  [HttpPost]
  public JsonResult AjaxFileUp03()
  {
   var data = new MoData { Msg = "上传失败" };
   try
   {
    var files = Request.Form.Files.Where(b => b.Name == "MyPhoto03");
    //非空限制
    if (files == null || files.Count() <= 0) { data.Msg = "请选择上传的文件。"; return Json(data); }
    //格式限制
    var allowType = new string[] { "image/jpeg", "image/png" };
    if (files.Any(b => !allowType.Contains(b.ContentType)))
    {
     data.Msg = $"只能上传{string.Join(",", allowType)}格式的文件。";
     return Json(data);
    }
    //大小限制
    if (files.Sum(b => b.Length) >= 1024 * 1024 * 4)
    {
     data.Msg = "上传文件的总大小只能在4M以下。"; return Json(data);
    }
    //初始化上传多个文件的Bar,存储到缓存中,方便获取上传进度
    var listBar = new List<MoBar>();
    files.ToList().ForEach(b =>
    {
     listBar.Add(new MoBar
     {
      FileName = b.FileName,
      Status = 1,
      CurrBar = 0,
      TotalBar = b.Length
     });
    });
    _cache.Set<List<MoBar>>(cacheKey03, listBar);
    var len = files.Count();
    Task[] tasks = new Task[len];
    //写入服务器磁盘
    for (int i = 0; i < len; i++)
    {
     var file = files.Skip(i).Take(1).SingleOrDefault();
     tasks[i] = Task.Factory.StartNew((p) =>
     {
      var item = p as IFormFile;
      //总大小
      var totalSize = item.Length;
      //初始化每次读取大小
      var readSize = 1024L;
      var bt = new byte[totalSize > readSize ? readSize : totalSize];
      //当前已经读取的大小
      var currentSize = 0L;
      var fileName = item.FileName;
      var path = Path.Combine(@"D:\F\学习\vs2017\netcore\netcore01\WebApp01\wwwroot\myfile", fileName);
      using (var stream = System.IO.File.Create(path))
      {
       //进度条处理流程
       using (var inputStream = item.OpenReadStream())
       {
        //读取上传文件流
        while (inputStream.Read(bt, 0, bt.Length) > 0)
        {
         //当前读取的长度
         currentSize += bt.Length;
         //写入上传流到服务器文件中
         stream.Write(bt, 0, bt.Length);
         //获取每次读取的大小
         readSize = currentSize + readSize <= totalSize ?
           readSize :
           totalSize - currentSize;
         //重新设置
         bt = new byte[readSize];
         //设置当前上传的文件进度,并重新缓存到进度缓存中
         var bars = _cache.Get<List<MoBar>>(cacheKey03);
         var currBar = bars.Where(b => b.FileName == fileName).SingleOrDefault();
         currBar.CurrBar = currentSize;
         currBar.Status = currentSize >= totalSize ? 2 : 1;
         _cache.Set<List<MoBar>>(cacheKey03, bars);
         System.Threading.Thread.Sleep(1000 * 1);
        }
       }
      }
     }, file);
    }
    //任务等待 ,这里必须等待,不然会丢失上传文件流
    Task.WaitAll(tasks);
    data.Msg = "上传完成";
    data.Status = 2;
   }
   catch (Exception ex)
   {
    data.Msg = ex.Message;
   }
   return Json(data);
  }

至于获取上传进度的Action也仅仅只是读取缓存数据而已:


[HttpPost]
  public JsonResult ProgresBar03()
  {
   var bars = new List<MoBar>();
   try
   {
    bars = _cache.Get<List<MoBar>>(cacheKey03);
   }
   catch (Exception ex)
   {
   }
   return Json(bars);
  }

这里再给出上传进度的实体类:


public class MoData
 {
  /// <summary>
  /// 0:失败 1:上传中 2:成功
  /// </summary>
  public int Status { get; set; }

  public string Msg { get; set; }
 }

 public class MoBar : MoData
 {
  /// <summary>
  /// 文件名字
  /// </summary>
  public string FileName { get; set; }

  /// <summary>
  /// 当前上传大小
  /// </summary>
  public long CurrBar { get; set; }

  /// <summary>
  /// 总大小
  /// </summary>
  public long TotalBar { get; set; }

  /// <summary>
  /// 进度百分比
  /// </summary>
  public string PercentBar
  {
   get
   {
    return $"{(this.CurrBar * 100 / this.TotalBar)}%";
   }
  }
 }

到此task任务处理上传文件的方式就完成了,咋们来看图看效果吧:

能够通过示例3和4的效果图对比出,没使用Task和使用的效果区别,这样效果您值得拥有,耐心读完本文内容的朋友,没让你失望吧,如果可以不妨点个"赞"或扫个码支持下作者,谢谢;内容最后附上具体测试用例代码:.NetCore上传多文件的几种示例

The above is the detailed content of Detailed explanation of the example of uploading multiple files using .NetCore. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn