آپلود فایل و حذف فایل در ASP.NET Core – جلسه ۵۲

Working with files in ASP.NET Core MVC - Session 52

در این جلسه، به بررسی روش آپلود فایل در ASP.NET Core و همچنین حذف فایل در ASP.NET Core خواهیم پرداخت و این قابلیت را بروی وب اپلیکشن خود توسعه خواهیم داد. همچنین برای فایل مورد نظر یک نام یکتا با استفاده از کلاس Guid ایجاد خواهیم کرد. سپس برای دسترسی به فایل مربوط به رکورد مورد نظر، اقدام به ذخیره سازی نام یکتا فایل در پایگاه داده میکنیم.

Guid و یا Globally Unique Identifier یک رشته متنی 128 بیتی میباشد که همانگونه که از نام آن مشخص میباشد یه شناسه جهانی یکتا است. توسعه دهندگان جهت ارایه شناسه یکتا در کامپیوتر، شبکه و یا رکورد های پایگاه داده از آن استفاده میکنند. عبارت رشته ایی Guid مانند یک شماره سریال میباشد و همانگونه که ذکر شد جهت جلوگیری از ایجاد نام و یا شناسه تکراری از آن استفاده میشود.

Guid چیست؟

Guid و یا Globally Unique Identifier یک رشته متنی 128 بیتی میباشد که همانگونه که از نام آن مشخص میباشد یه شناسه جهانی یکتا است. توسعه دهندگان جهت ارایه شناسه یکتا در کامپیوتر، شبکه و یا رکورد های پایگاه داده از آن استفاده میکنند.

آپلود فایل در ASP.NET Core

جهت آپلود فایل در ASP.NET Core MVC ابتدا نیاز به ارسال فایل از نما به کنترلر خواهیم داشت. برای ارسال فایل ابتدا میبایست به مدل و یا ViewModel خود یک فیلد از نوع IFormFile اضافه نماییم. به علاوه، خصوصیت enctype تگ فرم در نمای متناظر میبایست برابر با multipart/form-data باشد. سپس برای فیلد تعریف شده از نوع IFormFile یک المان input در نظر گرفته و با asp-for آنرا به فیلد مربوطه متصل میکنیم.

ما در این دوره آموزشی ابتدا فلید مربوطه را به CreateCostViewModel اضافه نمودیم.

    public class CreateCostViewModel
    {
        [Column(TypeName = "decimal(18, 2)")]
        [Required]
        [DataType(DataType.Currency)]
        public decimal Amount { get; set; }
        [Required]
        [DataType(DataType.Date)]
        [Display(Name = "Registered Data")]
        public DateTime RegisteredDate { get; set; }
        [MaxLength(150)]
        public string Comment { get; set; }
        [Required]
        [Display(Name = "Category")]
        [RegularExpression("^\\d+$", ErrorMessage ="Please select a category")]
        public int CategoryID { get; set; }
        [Display(Name = "Payment Method")]
        [Required(ErrorMessage ="This option is mandetory, please select a payment method")]
        public PaymentMethods? PaymentMethod { set; get; }
        public IFormFile UploadFile { get; set; }
    }

سپس تغییرات را در این مرحله بر‌‌‌‌روی نمای Create اعمال نمودیم.

@model CreateCostViewModel

@{
    ViewBag.Title = "Create Cost";
}
<form enctype="multipart/form-data" asp-controller="Cost" asp-action="Create" method="post" class="mt-5 offset-3 col-6 border border-primary pt-2 pb-2 px-2 py-2">
    <div>
        <label asp-for="Amount" class="form-label"></label>
        <input asp-for="Amount" class="form-control" />
        <span asp-validation-for="Amount" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="RegisteredDate" class="form-label"></label>
        <input asp-for="RegisteredDate" class="form-control" />
        <span asp-validation-for="RegisteredDate" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="Comment" class="form-label"></label>
        <input asp-for="Comment" class="form-control" />
        <span asp-validation-for="Comment" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="CategoryID" class="form-label"></label>
        <select asp-for=CategoryID asp-items="@ViewBag.Categories" class="form-select"></select>
        <span asp-validation-for="CategoryID" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="PaymentMethod" class="form-label"></label>
        <select asp-for="PaymentMethod" class="form-select" asp-items="@ViewBag.PaymentMethods"></select>
        <span asp-validation-for="PaymentMethod" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="UploadFile" class="form-label"></label>
        <input asp-for="UploadFile" class="form-control" />
    </div>
    <div><button type="submit" class="btn btn-primary mt-2">Create</button></div>
</form>

حال نوبت به ایجاد تغییرات بروی کنترلر مورد نظر میرسد. ابتدا جهت دسترسی به محل وب اپلیکیشن میتوانیم یک شئی از نوع IWebHostEnvironment درون سازنده کنترلر تزریق نمود. سپس میبایست بررسی شود که داده پست شده از فرم به کنترلر حاوی فایل میباشد یا خیر. در صورت وجود فایل با استفاده از Guid، یک نام یکتا برای فایل مورد نظر ایجاد مینماییم. در مرحله بعد مسیر آپلود فایل را با کمک شیء IWebHostEnvironment تعیین میکنیم. سپس جهت کپی فایل نیاز به ایجاد یک FileStream خواهیم داشت و در پایان با استفاده از متد CopyTo از شیءIFormFile اقدام به کپی فایل در مسیر مورد نظر میکنیم. در صورت نیاز میتوانیم نام یکتا فایل را جهت ذخیره سازی به پایگاه داده ارسال نماییم.

ما در این پروژه، ابتدا شیء IWebHostEnvironment را درون کنترلر CostController تزریق میکنیم.

        private readonly ICostRepository costRepository;
        private readonly ICategoryRepository categoryRepository;
        private readonly IWebHostEnvironment webHostEnvironment;

        public CostController(ICostRepository _costRepository, ICategoryRepository _categoryRepository, 
            IWebHostEnvironment _webHostEnvironment)
        {
            costRepository = _costRepository;
            categoryRepository = _categoryRepository;
            webHostEnvironment = _webHostEnvironment;
        }

سپس با توجه به نیاز چندباره به استفاده از قطعه کد آپلود فایل، برای آن یک متد درون کنترلر CostController ایجاد میکنیم. همانگونه که در قطعه کد زیر مشاهده میکنید، این متد وظیفه ایجاد نام فایل یکتا، تعیین مسیر آپلود فایل و همچنین FileStream و عملیات کپی را برعهده دارد.

        private string UploadFile(IFormFile formFile)
        {
            string UniqueFileName = Guid.NewGuid().ToString() + "-" + formFile.FileName;
            string TargetPath = Path.Combine(webHostEnvironment.WebRootPath, "images", UniqueFileName);
            using (var stream = new FileStream(TargetPath, FileMode.Create))
            {
                formFile.CopyTo(stream);
            }
            return UniqueFileName;
        }

سپس درون اکشن متد Create با متد HttpPost به بررسی وجود فایل و ارسال فایل به متد UploadFile خواهیم پرداخت. همچنین نام یکتا فایل توسط فیلد InvoiceImagePath از مدل Cost درون پایگاه داده ذخیره میگردد.

        [HttpPost]
        public IActionResult Create(CreateCostViewModel model)
        {
            if (ModelState.IsValid)
            {
                Cost cost = new()
                {
                    Amount = model.Amount,
                    Comment = model.Comment,
                    RegisteredDate = model.RegisteredDate,
                    CategoryID = model.CategoryID,
                    PaymentMethod = model.PaymentMethod
                };
                if(model.UploadFile != null)
                {
                    cost.InvoiceImagePath = UploadFile(model.UploadFile);
                }
                costRepository.Create(cost);
                return RedirectToAction("Index");
            }
            LoadDropdownList();
            return View(model);
        }

حذف فایل در ASP.NET Core MVC

جهت حذف فایل تنها نیاز به مسیر فایل مورد نظر میباشد. سپس با استفاده از System.IO.File.Delete میتوانیم اقدام به حذف فایل مورد نظر نماییم.

ما در این پروژه، از حذف قابلیت حذف فایل در دو اکشن متد Update و Delete از کنترلر CostController استفاده مینماییم. برای اکشن متد Update ابتدا میبایست یک ViewModel جدید ایجاد نماییم که حاوی IFormFile باشد. با توجه به مشترک بودن اکثر فیلدهای این ViewModel و CreateCostViewModel میتوانیم از ارث بری استفاده نماییم.

    public class UpdateCostViewModel : CreateCostViewModel
    {
        public int ID { get; set; }
        public string ExsitingFile { get; set; }
    }

سپس نیاز به اعمال تغییراتی در نمای Update جهت اضافه نمودن تگ input به منظور بارگزاری عکس و نمایش عکس احتمالی رکورد مورد نظر داریم.

@model UpdateCostViewModel
@{
    ViewBag.Title = "Create Cost";
    var imagepath = "~/images/" + (Model.ExsitingFile ?? "no-image.png");
}
<form asp-controller="Cost" enctype="multipart/form-data" asp-action="Update" method="post" class="mt-5 offset-3 col-6 border border-primary pt-2 pb-2 px-2 py-2">
    <input hidden asp-for="ID" />
    <div>
        <label asp-for="Amount" class="form-label"></label>
        <input asp-for="Amount" class="form-control" />
        <span asp-validation-for="Amount" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="RegisteredDate" class="form-label"></label>
        <input asp-for="RegisteredDate" class="form-control" />
        <span asp-validation-for="RegisteredDate" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="Comment" class="form-label"></label>
        <input asp-for="Comment" class="form-control" />
        <span asp-validation-for="Comment" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="CategoryID" class="form-label"></label>
        <select asp-for=CategoryID asp-items="@ViewBag.Categories" class="form-select"></select>
        <span asp-validation-for="CategoryID" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="PaymentMethod" class="form-label"></label>
        <select asp-for="PaymentMethod" class="form-select" asp-items="@ViewBag.PaymentMethods"></select>
        <span asp-validation-for="PaymentMethod" class="text-danger"></span>
    </div>
    <div>
        <label asp-for="UploadFile" class="form-label"></label>
        <input asp-for="UploadFile" class="form-control" />
    </div>
    <img src="@imagepath" asp-append-version="true" class="img-thumbnail"/>
    <div class="row mt-2">
        <div class="col-3 d-grid">
            <button type="submit" class="btn btn-primary">Edit</button>
        </div>
        <div class="col-3 d-grid">
            <a asp-action="index" class="btn btn-info">Cost List</a>
        </div>
    </div>
</form>

همانگونه که در کد مربوط به نمای Update مشاهده میکنید، جهت نمایش فایل تصویر، یک شیء با نام imagepath ایجاد نمودیم و در تگ img در قسمت scr از آن استفاده کردیم. همچنین ما در نمای Detail جهت نمایش عکس از این روش استفاده کردیم که کد نمای Detail در زیر قابل مشاهده است.

@model Cost
@{
    ViewBag.Title = "Cost Detail";
    var imagepath = "~/images/" + (Model.InvoiceImagePath ?? "no-image.png");
}
<div class="row">
    <div class="col-lg-4 offset-lg-4 col-md-6 offset-md-3 col-12">
        <div class="card">
            <img  src="@imagepath" class="card-img-top" asp-append-version="true"/>
            <div class="card-header">
                Detail Cost ID @Model.ID
            </div>
            <div class="card-body">
                <p class="card-text">Amount: @Model.Amount</p>
                <p class="card-text">Category: @Model.CategoryID</p>
                <p class="card-text">Comment: @Model.Comment</p>
                <p class="card-text">Payment Method: @Model.PaymentMethod</p>
                <a class="btn btn-primary" asp-controller="cost" asp-action="index">Cost List</a>
            </div>
        </div>
    </div>
</div>

حال با توجه به تغییر مدل در نمای Update، نیاز به اعمال تغییراتی در کنترلر CostController و اکشن متد Update برای هر دو متد httpPost و HttpGet داریم.

    [HttpGet]
    public IActionResult Update(int id)
    {
        var cost = costRepository.GetCostByID(id);
        UpdateCostViewModel model = new()
        {
            ID = cost.ID,
            Amount = cost.Amount,
            RegisteredDate = cost.RegisteredDate,
            Comment = cost.Comment,
            CategoryID = cost.CategoryID,
            PaymentMethod = cost.PaymentMethod,
            ExsitingFile = cost.InvoiceImagePath
        };
        LoadDropdownList();
        return View(model);
    }

در اکشن متد Update با متد HttpPost میبایست بررسی کنیم که آیا فایل جدیدی جهت بارگزاری وجود دارد یا خیر. همچنین باید بررسی کنیم در صورت ارسال فایل جدید، رکورد مورد نظر تصویر قبلی وجود داشته یا خیر. سپس در صورت وجود تصویر قدیمی، ابتدا اقدام به حذف آن کرده و سپس تصویر جدیدی را آپلود میکنیم. ضمنا در صورت عدم وجود تصویر قدیمی، فقط تصویر جدید را بروزرسانی میکنیم.

        [HttpPost]
        public IActionResult Update(UpdateCostViewModel model)
        {
            if (ModelState.IsValid)
            {
                Cost UpdatedCost = costRepository.GetCostByID(model.ID);
                UpdatedCost.Amount = model.Amount;
                UpdatedCost.RegisteredDate = model.RegisteredDate;
                UpdatedCost.Comment = model.Comment;
                UpdatedCost.CategoryID = model.CategoryID;
                UpdatedCost.PaymentMethod = model.PaymentMethod;
                if(model.UploadFile != null)
                {
                    if(UpdatedCost.InvoiceImagePath != null)
                    {
                        string ExitingFile = Path.Combine(webHostEnvironment.WebRootPath, "images", UpdatedCost.InvoiceImagePath);
                        System.IO.File.Delete(ExitingFile);
                    }
                    UpdatedCost.InvoiceImagePath = UploadFile(model.UploadFile);
                }
                costRepository.Update(UpdatedCost);
                return RedirectToAction("Index");
            }
            LoadDropdownList();
            return View(model);
        }

همچنین میبایست عملیات حذف فایل را بر روی اکشن متد Delete نیز اعمال نماییم. پس در زمان حذف رکورد، بررسی میکنیم که در صورت وجود فایل متناظر، علاوه بر حذف رکورد میبایست فایل متناظر نیز حذف گردد.

        [HttpPost]
        public IActionResult Delete(int id)
        {
            Cost cost = costRepository.GetCostByID(id);
            if (cost.InvoiceImagePath != null)
            {
                string ExitingFile = Path.Combine(webHostEnvironment.WebRootPath, "images", cost.InvoiceImagePath);
                System.IO.File.Delete(ExitingFile);
            }
            costRepository.Delete(id);
            return RedirectToAction("index");
        }

در صورت نیاز به جزئیات بیشتر، میتوانید ویدئو آموزشی این جلسه را تماشا نمایید. همچنین برای آگاهی از جلسات بعدی این دوره آموزشی، ما را در اینستاگرام، تلگرام، یوتیوب و آپارات دنبال کنید. ضمنا لیست کامل جلسات در این قسمت در دسترس شما میباشد و سورس کد این جلسه را میتوانید از GitHub ما دانلود نمایید.

تماشای ویدیو در یوتیوب ما

دانلود اسلایدهای آموزشی این جلسه از اینجا

برچسب ها

5 2 رای ها
امتیازدهی به مقاله
اشتراک در
اطلاع از
guest
0 نظرات
بازخورد (Feedback) های اینلاین
مشاهده همه دیدگاه ها
0
افکار شما را دوست داریم، لطفا نظر دهید.x