在 ASP.NET Core MVC中批量上传多个文件

admin
admin
2021-06-03
分享:

本文作者:梁桐铭- 微软最有价值专家(Microsoft MVP)
本文出自《从零开始学 ASP.NET Core 与 EntityFramework Core》目录
视频课程效果更佳:跨平台开发实战掌握 ASP.NET Core 与 EntityFramework Core

在 ASP.NET Core MVC 完成上传多个文件

在上个章节中,我们已经学习了单个文件的上传,本章我们将学习如果完成多文件的上传内容。

我们依然使用 添加学生的表单视图,当我们点击请选择照片按钮的时候,我们将会通过他来完成多文件的上传。

被我们选中的照片则会被存储到 Web 服务器上的wwwroot/images文件夹中

images

提交信息后,我们能够在数据库Students表中,存储 Student 类的数据,包含Name,Email,Major,PhotoPath信息。

下面是数据库 Students 表中的信息,是通过 ASP.NET Core 中的迁移功能来创建的。

Id 名字 电子邮件 主修科目 图片路径
1 52ABP管理员 2 info@ddxc.org info.png
2 梁桐铭 3 ltm@ddxc.org ltm.png

为了实现多个文件的上传功能,我们需要添加修改和添加以下几个类文件。

StudentCreateViewModel 文件

 public class StudentCreateViewModel
    {

        [Required(ErrorMessage = "请输入名字"), MaxLength(50, ErrorMessage = "名字的长度不能超过50个字符")]
        [Display(Name = "名字")]
        public string Name { get; set; }
        [Required]
        [Display(Name = "主修科目")]
        public MajorEnum? Major { get; set; }

        [Display(Name = "电子邮件")]
        [RegularExpression(@"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
        ErrorMessage = "邮箱的格式不正确")]
        [Required(ErrorMessage = "请输入邮箱地址")]
        public string Email { get; set; }

        [Display(Name = "图像")]
         public List<IFormFile> Photos { get; set; }
    }

我们要将StudentCreateViewModel文件中的IFormFile类型的 Photos 的字段修改为 List<IFormFile>

  • IFormFile 位于Microsoft.AspNetCore.Http命名空间中。
  • 上传至服务器的文件可通过 IFormFile 接口通过模型绑定的形式进行访问。
  • Photos 属性通过模型绑定接收上传的文件
  • 因为要支持多个文件上传,我们将 Photos 属性的数据类型设置为 List<IFormFile>

更新 Create 视图的代码

@model StudentCreateViewModel
@{ ViewBag.Title = "创建学生信息"; }

要支持文件上传,请设置表单元素为enctype="multipart/form-data"

  • enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。
    • application/x-www-form-urlencoded 在发送前编码所有字符(默认)
    • multipart/form-data 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
    • text/plain 空格转换为 "+" 加号,但不对特殊字符编码。 以上都是 Hhtml 的基础。
<form
  enctype="multipart/form-data"
  asp-controller="home"
  asp-action="create"
  method="post"
  class="mt-3"
>
  <div class="form-group row">
    <label asp-for="Name" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
      <input asp-for="Name" class="form-control" placeholder="请输入名字"/>
      <span asp-validation-for="Name" class="text-danger"></span>
    </div>
  </div>

  <div class="form-group row">
    <label asp-for="Email" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
      <input
        asp-for="Email"
        class="form-control"
        placeholder="请输入邮箱地址"
     />
      <span asp-validation-for="Email" class="text-danger"></span>
    </div>
  </div>

  <div class="form-group row">
    <label asp-for="Major" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
      <select
        asp-for="Major"
        class="custom-select mr-sm-2"
        asp-items="Html.GetEnumSelectList<MajorEnum>()"
      >
        <option value=""> 请选择</option>
      </select>
      <span asp-validation-for="Major" class="text-danger"></span>
    </div>
  </div>

  @* 我们使用了asp-for的taghelper设置input的属性为"Photos"。
  "Photos"属性类型是`List<IFormFile>`, 所以在运行的时候ASP.NET 
  Core会将该标签生成上传控件(input type=file) 而要支持多个文件上传,需要
  multiple 属性支持*@

  <div class="form-group row">
    <label asp-for="Photos" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
      <div class="custom-file">
        <input
          asp-for="Photos"
          multiple
          class="form-control custom-file-input"
       />
        <label class="custom-file-label">请选择照片...</label>
      </div>
    </div>
  </div>

  <div asp-validation-summary="All" class="text-danger"></div>
  <div class="form-group row">
    <div class="col-sm-10">
      <button type="submit" class="btn btn-primary">创建</button>
    </div>
  </div>
  @*以下JavaScript代码是必须的,它的作用是 -
  如果选择了单个文件,则显示该文件的名称。 -
  如果多个选择文件,然后显示文件数量。 *@ @section Scripts {
  <script>
    $(document).ready(function() {
      $(".custom-file-input").on("change", function() {
       //console.log($(this));
        var fileLabel = $(this).next(".custom-file-label");
        var files = $(this)[0].files;
        if (files.length > 1) {
          fileLabel.html("您已经选择了:" + files.length + " 个文件");
        } else if (files.length == 1) {
          fileLabel.html(files[0].name);
        }
      });
    });
  </script>
  }
</form>

我们使用了 asp-for 的 taghelper 设置 input 的属性为"Photos"。 "Photos"属性类型是List<IFormFile>, 所以在运行的时候 ASP.NET Core 会将该标签生成上传控件(input type=file) 而要支持多个文件上传,需要 multiple 属性支持,参考以下代码:

<input asp-for="Photos" multiple class="form-control custom-file-input" />

生成 HTML 后

<input
  multiple
  class="form-control custom-file-input"
  type="file"
  id="Photos"
  name="Photos"
/>
  • 因为原有的Student领域模型已经不满足我们的业务呈现了,所以当前页面视图模型改用StudentCreateViewModel

  • 在结束form标签之前的 JavaScript 代码我们也做了修改,它的作用是,

    • 如果选择了单个文件,则显示该文件的名称。
    • 如果多个选择文件,然后显示文件数量。

修改 Create 操作方法

我们回到HomeController文件中,因为要让 Create()方法支持多文件上传,所以我们要对这个方法进行修改。修改规则如下:

  • 判断用户是否上传了图片,如果没有上传图片路径信息为空。
    • 所以我们要判断StudentCreateViewModel中的Photo属性是否为空
  • 如果有上传图片,则要进行规则验证。
  • 所有的图片都必须上传到 wwwroot 中的 images 文件夹中。
    • 而要获取wwwroot文件夹的路径,我们需要通过 ASP.NET Core 中的依赖注入注册 HostingEnvironment服务
  • 为了确保文件名是唯一的,文件名的生成规则为 GUID 值加一个下划线。

完整的上传文件代码如下:

 [HttpPost]
public IActionResult Create(StudentCreateViewModel model)
        {
            if (ModelState.IsValid)
            {
                string uniqueFileName = null;

//如果传入模型对象中的Photo属性不为null,并且Count>0,则表示用户选择至少一个要上传的文件。
                if (model.Photos != null && model.Photos.Count > 0)
                {
                   //循环每个选定的文件
                    foreach (IFormFile photo in model.Photos)
                    {
  //必须将图像上传到wwwroot中的images文件夹
    //而要获取wwwroot文件夹的路径,我们需要注入 ASP.NET  Core提供的HostingEnvironment服务
      //通过HostingEnvironment服务去获取wwwroot文件夹的路径
    string uploadsFolder = Path.Combine(hostingEnvironment.WebRootPath, "images");
   //为了确保文件名是唯一的,我们在文件名后附加一个新的GUID值和一个下划线
      uniqueFileName = Guid.NewGuid().ToString() + "_" + photo.FileName;
      string filePath = Path.Combine(uploadsFolder, uniqueFileName);
     //使用IFormFile接口提供的CopyTo()方法将文件复制到wwwroot/images文件夹
      photo.CopyTo(new FileStream(filePath, FileMode.Create));
                    }

                }
                Student newStudent = new Student
                {
                    Name = model.Name,
                    Email = model.Email,
                    Major = model.Major,
      // 将文件名保存在student对象的PhotoPath属性中 它将保存到数据库 Students的 表中
                    PhotoPath = uniqueFileName
                };

                _studentRepository.Add(newStudent);
                return RedirectToAction("details", new { id = newStudent.Id });
            }

            return View();
        }


HomeController文件中的其余代码不需要上传文件。涉及到图片显示的视图页面为学生详情页面学生列表页面,所以去修改对应的代码地方即可。

文章说明

如果您觉得我的文章质量还不错,欢迎打赏,也可以订阅我的视频哦
未得到授权不得擅自转载本文内容,52abp.com 保留版权
感谢您对我的支持

关注微信公众号:角落的白板报

公众号:角落的白板报