ASP.NET MVC 檔案上傳下載刪除功能

ASP.NET MVC

雖然現在檔案上傳下載網路上有很多範例,但範例很陽春…這一次筆者要將這些功能整合起來,這些功能在內部系統或後台系統實用非常高,也是實際上最常見需求,本例子用非常簡易方式來陳現。

這樣不用老是遇到問題,就去問google老大,成品會如下…

這一次就從新的專案MVC 5開始進行開發這一整個檔案管理系統機制、這個功能已經具備CRUD以外,包含了檔案的上傳、下載、刪除的機制。

Model分別有兩個Class,一個是Person和AppendFile

    public class Person
    {
        public int Id { get; set; }
        [Required(ErrorMessage = "請填寫姓名")]
        [DisplayName("姓名")]
        public string Name { get; set; }
        [Required(ErrorMessage = "請填寫備註")]
        [DisplayName("備註")]
        public string Remark { get; set; }
        
        public virtual ICollection<AppendFile> AppendFiles { get; set; }
    }
    public class AppendFile
    {
        public Guid Id { get; set; }
        public string FileName { get; set; }
        public string Extension { get; set; }
        public int PersonId { get; set; }
        public virtual Person Person { get; set; }
    }

上述是EF的一對多關聯設計。

    public class PersonContext:DbContext
    {
        public PersonContext()
       : base("name=DefaultConnection")
        {
        }
        public DbSet<Person> Persons { get; set; }
        public DbSet<AppendFile> AppendFiles { get; set; }
    }

WebConfig底下

   <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=localhost;Initial Catalog=UploadFileDemo;Integrated Security=True" providerName="System.Data.SqlClient" /> 
  </connectionStrings>

最後開始進行做資料移轉

 Enable-Migrations
 Add-Migration InitCreateTable
 Update-Database

先開始撈取資料和建立Controller並建立檢視

        private PersonContext db = new PersonContext();
        public ActionResult Index()
        {
            return View(db.Persons.ToList());
        }
        public ActionResult Create()
        {
            return View();
        }

最後在Index中進行刻View畫面

@model IEnumerable<FileUpload.Models.Person>

@{ ViewBag.Title = "Index"; }

<h2>清單</h2>

<p>
    @Html.ActionLink("新增", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Remark)
        </th>
        <th>附件數</th>
        <th></th>
    </tr>

    @foreach (var item in Model)
    {
       <tr>
           <td>
               @Html.DisplayFor(modelItem => item.Name)
           </td>
           <td>
               @Html.DisplayFor(modelItem => item.Remark)
           </td>
           <td>
               @if (item.AppendFiles.Count() == 0)
               {
                   <span>無檔案</span> 
               }
               else
               {
               <span>@item.AppendFiles.Count() File(s)</span>
               }
           </td>
           <td>
               @Html.ActionLink("修改", "Edit", new { id = item.Id }) |
               <a href="javascript:void(0);" data-id="@item.Id" class="deleteItem">刪除</a>
           </td>
       </tr>
     }
</table>

@section Scripts {

    <script>
    </script>

}
@model FileUpload.Models.Person

@{ ViewBag.Title = "建立"; }

@using (Html.BeginForm("Create", "Home", null, FormMethod.Post,
    new { enctype = "multipart/form-data" }))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>建立個人資料與附件</legend>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.Remark)
        </div>
        <div class="editor-field">
            @Html.TextAreaFor(model => model.Remark)
            @Html.ValidationMessageFor(model => model.Remark)
        </div>
        <div class="editor-label">
            <label>Files:</label>
        </div>
        <div class="editor-field">
            <input type="file" name="file" multiple="multiple" />
        </div>
        <p>
            <input type="submit" value="儲存" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("回清單頁", "Index")
</div>

@section Scripts {
    <script src="~/Scripts/jquery.validate.min.js"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
}

最後寫Create Controller

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Person person)
        {
            if (ModelState.IsValid)
            {
                List<AppendFile> appendFiles = new List<AppendFile>();
                for (int i = 0; i < Request.Files.Count; i++)
                {
                    var file = Request.Files[i];

                    if (file != null && file.ContentLength > 0)
                    {
                        var fileName = Path.GetFileName(file.FileName);
                        AppendFile appendFile = new AppendFile()
                        {
                            FileName = fileName,
                            Extension = Path.GetExtension(fileName),
                            Id = Guid.NewGuid()
                        };
                        appendFiles.Add(appendFile);
                        var folder = Server.MapPath("~/App_Data/Upload/");
                        if (!Directory.Exists(folder))
                            Directory.CreateDirectory(folder);
                        
                        var path = Path.Combine(folder, appendFile.Id + appendFile.Extension);
                        file.SaveAs(path);
                    }
                }

                person.AppendFiles = appendFiles;
                db.Persons.Add(person);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(person);
        }

最後補上修改與刪除檔案、下載機制全貌,並加入修改的檢視處理

using FileUpload.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using System.Data.Entity;
namespace FileUpload.Controllers
{
    public class HomeController : Controller
    {
        private PersonContext db = new PersonContext();

        public ActionResult Index()
        {
            return View(db.Persons.ToList());
        }
        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Person person)
        {
            if (ModelState.IsValid)
            {
                List<AppendFile> appendFiles = new List<AppendFile>();
                for (int i = 0; i < Request.Files.Count; i++)
                {
                    var file = Request.Files[i];

                    if (file != null && file.ContentLength > 0)
                    {
                        var fileName = Path.GetFileName(file.FileName);
                        AppendFile appendFile = new AppendFile()
                        {
                            FileName = fileName,
                            Extension = Path.GetExtension(fileName),
                            Id = Guid.NewGuid()
                        };
                        appendFiles.Add(appendFile);
                        var folder = Server.MapPath("~/App_Data/Upload/");
                        if (!Directory.Exists(folder))
                            Directory.CreateDirectory(folder);
                        
                        var path = Path.Combine(folder, appendFile.Id + appendFile.Extension);
                        file.SaveAs(path);
                    }
                }

                person.AppendFiles = appendFiles;
                db.Persons.Add(person);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(person);
        }
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Person person = db.Persons.Include(p => p.AppendFiles).SingleOrDefault(x => x.Id == id);
            if (person == null)
            {
                return HttpNotFound();
            }
            return View(person);
        }
       
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Person person)
        {
            if (ModelState.IsValid)
            {

                //New Files
                for (int i = 0; i < Request.Files.Count; i++)
                {
                    var file = Request.Files[i];

                    if (file != null && file.ContentLength > 0)
                    {
                        var fileName = Path.GetFileName(file.FileName);
                        AppendFile appendFile = new AppendFile()
                        {
                            FileName = fileName,
                            Extension = Path.GetExtension(fileName),
                            Id = Guid.NewGuid()
                        };
                        var folder = Server.MapPath("~/App_Data/Upload/");
                        var path = Path.Combine(folder, appendFile.Id + appendFile.Extension);
                        file.SaveAs(path);

                        db.Entry(appendFile).State = EntityState.Added;
                    }
                }

                db.Entry(person).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(person);
        }
        [HttpPost]
        public JsonResult DeleteFile(string id)
        {
            if (String.IsNullOrEmpty(id))
            {
                Response.StatusCode = (int)HttpStatusCode.BadRequest;
                return Json(new { Result = "Error" });
            }
            try
            {
                Guid guid = new Guid(id);
                var appendFile = db.AppendFiles.Find(guid);
                if (appendFile == null)
                {
                    Response.StatusCode = (int)HttpStatusCode.NotFound;
                    return Json(new { Result = "Error" });
                }

                //Remove from database
                db.AppendFiles.Remove(appendFile);
                db.SaveChanges();
                var folder = Server.MapPath("~/App_Data/Upload/");
                //Delete file from the file system
                var path = Path.Combine(folder, appendFile.Id + appendFile.Extension);
                if (System.IO.File.Exists(path))
                {
                    System.IO.File.Delete(path);
                }
                return Json(new { Result = "OK" });
            }
            catch (Exception ex)
            {
                return Json(new { Result = "ERROR", Message = ex.Message });
            }
        }


        [HttpPost]
        public JsonResult Delete(int id)
        {
            try
            {
                Person person = db.Persons.Find(id);
                if (person == null)
                {
                    Response.StatusCode = (int)HttpStatusCode.NotFound;
                    return Json(new { Result = "Error" });
                }

                //delete files from the file system
                var folder = Server.MapPath("~/App_Data/Upload/");
                foreach (var item in person.AppendFiles)
                {
                    String path = Path.Combine(folder, item.Id + item.Extension);
                    if (System.IO.File.Exists(path))
                    {
                        System.IO.File.Delete(path);
                    }
                }

                db.Persons.Remove(person);
                db.SaveChanges();
                return Json(new { Result = "OK" });
            }
            catch (Exception ex)
            {
                return Json(new { Result = "ERROR", Message = ex.Message });
            }
        }
        public FileResult Download(String p, String d)
        {
            return File(Path.Combine(Server.MapPath("~/App_Data/Upload/"), p), System.Net.Mime.MediaTypeNames.Application.Octet, d);
        }
    }
   
}
@model FileUpload.Models.Person

@{ ViewBag.Title = "Edit"; }

@using (Html.BeginForm("Edit", "Home", null, FormMethod.Post,
            new { enctype = "multipart/form-data" }))
{
       @Html.AntiForgeryToken()
       @Html.ValidationSummary(true)

           <fieldset>
               <legend>修改資料</legend>

               @Html.HiddenFor(model => model.Id)

               <div class="editor-label">
                   @Html.LabelFor(model => model.Name)
               </div>
               <div class="editor-field">
                   @Html.EditorFor(model => model.Name)
                   @Html.ValidationMessageFor(model => model.Name)
               </div>

               <div class="editor-label">
                   @Html.LabelFor(model => model.Remark)
               </div>
               <div class="editor-field">
                   @Html.TextAreaFor(model => model.Remark)
                   @Html.ValidationMessageFor(model => model.Remark)
               </div>
               <div class="editor-label">
                   <label>Files:</label>
               </div>
               <div class="editor-field">
                   <input type="file" name="file" multiple="multiple" />

                   <ul class="attachment">
                       @foreach (var item in Model.AppendFiles)
                       {
                         <li>
                             <a class="title" href="/Home/Download/?p=@(item.Id + item.Extension)&d=@item.FileName">@item.FileName</a>
                             <a href="javascript:void(0);" data-id="@item.Id" class="deleteItem">刪除檔案</a>
                         </li>
                       }
                   </ul>
               </div>
               <p>
                   <input type="submit" value="儲存" />
               </p>
           </fieldset>}

<div>
    @Html.ActionLink("回清單", "Index")
</div>

@section Scripts {
    <script src="~/Scripts/jquery.validate.min.js"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

    <script>
        $('.deleteItem').click(function (e) {
            e.preventDefault();
            var $ctrl = $(this);
            if (confirm('是否刪除此筆檔案?')) {
                $.ajax({
                    url: '@Url.Action("DeleteFile")',
                    type: 'POST',
                    data: { id: $(this).data('id') }
                }).done(function (data) {
                    if (data.Result == "OK") {
                        $ctrl.closest('li').remove();
                    }
                    else if (data.Result.Message) {
                        alert(data.Result.Message);
                    }
                }).fail(function () {
                    
                })

            }
        });
    </script>

}

最後回頭再補上首頁的刪除詢問項目

@model IEnumerable<FileUpload.Models.Person>

@{ ViewBag.Title = "Index"; }

<h2>清單</h2>

<p>
    @Html.ActionLink("新增", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Remark)
        </th>
        <th>附件數</th>
        <th></th>
    </tr>

    @foreach (var item in Model)
    {
       <tr>
           <td>
               @Html.DisplayFor(modelItem => item.Name)
           </td>
           <td>
               @Html.DisplayFor(modelItem => item.Remark)
           </td>
           <td>
               @if (item.AppendFiles.Count() == 0)
               {
                   <span>無檔案</span> 
               }
               else
               {
               <span>@item.AppendFiles.Count() File(s)</span>
               }
           </td>
           <td>
               @Html.ActionLink("修改", "Edit", new { id = item.Id }) |
               <a href="javascript:void(0);" data-id="@item.Id" class="deleteItem">刪除</a>
           </td>
       </tr>
     }
</table>

@section Scripts 
    {

    <script>
      $('.deleteItem').click(function (e) {
            e.preventDefault();
            var $ctrl = $(this);
            if (confirm('確認要刪除該項目?')) {
                $.ajax({
                    url: '@Url.Action("Delete")',
                    type: 'POST',
                    data: { id: $(this).data('id') }
                }).done(function (data) {
                    if (data.Result == "OK") {
                        $ctrl.closest('tr').remove();
                    }
                    else if (data.Result.Message) {
                        alert(data.Result.Message);
                    }
                }).fail(function () {
                    
                })

            }
        });
    </script>

}

以上是非常完整的檔案管理系統

IT界的影武者