Maison  >  Article  >  développement back-end  >  Explication détaillée de l'exemple de téléchargement de plusieurs fichiers à l'aide de .NetCore

Explication détaillée de l'exemple de téléchargement de plusieurs fichiers à l'aide de .NetCore

Y2J
Y2Joriginal
2017-04-19 16:36:252527parcourir

Ce chapitre partage avec vous un exemple de téléchargement de fichiers à l'aide du framework MVC de .NetCore. Le contenu principal comprend : la soumission du formulaire pour le téléchargement, le téléchargement ajax, la soumission ajax + l'effet de progression du téléchargement, le traitement parallèle des tâches + la soumission ajax + la progression du téléchargement. est très utile pour tout le monde. Les amis intéressés peuvent suivre l'éditeur pour apprendre ensemble

Ce chapitre partage avec vous un exemple de téléchargement de fichiers à l'aide du framework MVC de .NetCore. Le contenu principal est : la soumission et le téléchargement de formulaires, ajax. Téléchargement, soumission ajax + effet de progression du téléchargement, traitement parallèle des tâches + soumission ajax + progression du téléchargement. Je pense que vous obtiendrez une bonne récolte après avoir lu le contenu de l'article. Si vous le pouvez, vous pourriez aussi bien lui donner un like car le. L'ordinateur est tombé en panne hier, je vais presque finir de l'écrire. Le contenu n'a pas été enregistré, alors je suis arrivé dans l'entreprise tôt ce matin et j'ai recommencé à zéro. La panne de courant a vraiment été un casse-tête, mais pour le bien de la communauté. environnement de partage, cela en valait la peine. Sans plus tarder, passons à la partie principale d'aujourd'hui

Télécharger un ensemble d'images dans le formulaire

Jetons d'abord un coup d'œil à notre code HTML. Voici une brève explication de ce qui est requis pour télécharger des fichiers. Définissez l'attribut enctype="multipart/form-data" et la méthode de publication dans l'élément de formulaire. , vous devez définir l'élément file type='file' sur son attribut multiple='multiple', donc avec le contenu suivant :


<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>

En raison de la soumission du formulaire , ce scénario de test utilise uniquement le type=submit par défaut de l'élément bouton pour soumettre le formulaire, et l'arrière-plan correspondant Action Le code est le suivant :


/// <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();
 }

Action du front-end au back-end.Je dois dire que cette méthode de soumission de formulaire est assez simple.Il convient de noter que la méthode du modèle d'entité correspond aux informations du fichier téléchargé. ici, et l'attribut public Listbd57100a971f76ffcb06f3d1bfb26aea MyPhoto { get; set; } est utilisé pour correspondre au fichier type='file'的name nom d'attribut name="MyPhoto" sous la forme html :


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

De cette façon, le les informations sur le fichier téléchargé peuvent être stockées dans l'attribut MyPhoto dans la classe MoUser personnalisée via le modèle d'entité

Ajax télécharge un ensemble d'images

Ici, vous devez modifier certaines choses dans le code HTML dans l'exemple ci-dessus. Au lieu d'utiliser la soumission de formulaire, un bouton normal est spécifié pour déclencher la soumission ajax. Le code html complet est le suivant :


<.>

<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>
Avec la mise en page, jetons un coup d'œil au code d'implémentation js spécifique. Ici, j'utilise la méthode de soumission ajax de jquery pour fonctionner, et j'utilise également le nouveau FormData en html5 pour stocker les données :


Quant à la méthode Action en arrière-plan, elle n'est pas très différente de l'exemple 1. Le point clé est qu'ici j'utilise directement la méthode Request.Form.Files pour obtenir les fichiers téléchargés n'utilisent plus de modèles d'entité, de sorte que les cas de test sont plus diversifiés :
$("#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("上传文件异常,请稍后重试!");
  }
  });
 });


Si vous avez la patience de lire ceci, alors ce qui suit le contenu estime personnellement qu'il sera d'une grande aide à votre développement et qu'il sera à la hauteur de vos attentes
/// <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);
 }

soumission ajax + progression du téléchargement + un ensemble de téléchargements d'images De même, regardons d'abord le code html correspondant. En fait, c'est presque le même que l'exemple 2, sauf que le nom a été modifié :

<.>
Pour ajouter un effet de progression, vous avez besoin Le minuteur de js est utilisé pour obtenir régulièrement les informations sur les données de progression du fichier téléchargé. Par conséquent, la méthode setInterval de js est utilisée pour demander régulièrement une interface de données de progression. que vous devez effacer cette minuterie après utilisation, sinon elle continuera à demander votre interface :

<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>


Étant donné que l'interface de données de progression distincte est mentionnée ci-dessus, en plus de la Action de téléchargement, nous avons également besoin de l'action de progression et des données du fichier de téléchargement obtenues par l'action de progression. Les informations doivent être cohérentes avec l'action téléchargée, vous devez donc utiliser le cache et d'autres méthodes pour stocker les données. Ici, j'utilise la méthode MemoryCache. Pour netcore, il vous suffit de l'ajouter au fichier de démarrage (tel que Startup.cs). Service de composant :

 $("#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);
  });


puis injecté dans l'interface correspondante Controller. via le constructeur :

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


À ce stade, nous pouvons utiliser le cache pour stocker nos informations de progression du téléchargement. Jetons un coup d'œil à l'action pour traiter le téléchargement : <.>

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

Le code devient du coup beaucoup, en fait, selon la logique, il ajoute seulement un Cache pour stocker la progression, et la logique de lecture du flux de fichiers téléchargés un par un. Vous pouvez regarder attentivement le code, il y a des notes ; et puis il y a nos informations de progression Interface d'action :

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);
 }

L'interface de progression n'a besoin que de obtenez les informations de progression dans le cache. Remarque : Il s'agit d'un cas de test. Pour des scénarios d'utilisation spécifiques, veuillez ajouter d'autres codes logiques par vous-même ; voir ci-dessous. Capture d'écran de l'effet ci-dessous :

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

Traitement parallèle des tâches + soumission ajax + progression du téléchargement + un ensemble de téléchargements d'images

这一小节,将会使用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上传多文件的几种示例

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn