File Upload با استفاده از Ajax در Asp.net mvc

یکشنبه 1 آذر 1394

در این مقاله یک File Upload با استفاده از Ajax در Asp.net mvc خواهیم ساخت .این File Upload مانند حالت های معمولی که در MVC ایجاد می کنیم نیست و کمی با آن متفاوت است .

File Upload با استفاده از Ajax در Asp.net mvc

قرار دادن File Upload  اگر از postback کامل استفاده کنید بسیار ساده است .اما در اینجا حالتی که در آن از Ajax  استفاده می کنیم و یک  Partial را پر می کنیم، استفاده خواهیم کرد.

استفاده از JQuery برای ارسال یک فرم Post ساده است .اما در حالتی که قصد داریم فرمی که Post می کنیم multi-part استفاده کند و داخل آن هم یک فایل آپلود باشد کار خیلی ساده ای نیست .

در این مقاله از JQuery برای آپلود همزمان بدون دوباره لود کردن صفحه استفاده می کنیم .شاید اولین ایده ای که به ذهن برسد استفاده از Ajax.BeginForm باشد ولی این متد file upload توسط partial post-back را پشتیبانی نمی کند.این متد با Postback کار می کند.

دومین ایده استفاده از JQuery و Ajax است .در این مقاله از روش خاصی استفاده شده است که در آن Post شدن فایل در یک Partial انجام شده و این Partial توسط Ajax بازگردانده می شود.توسط متد IFrame شبیه سازی Postback ناهمزمان انجام می شود و به نظر می رسد که uploading توسط Ajax انجام شده است .

فرم اصلی که کد آن در زیر آورده شده است با استفاده از helper انجام شده است و در آخر یک iframe که در آن فرم ما لود خواهد شد آورده شده است

@{
    ViewBag.Title = "Ajax File Uploading";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Ajax File Uploading</h2>
<div>
    <div>
        <h1>Upload File</h1>
    </div>

    @using (Html.BeginForm("Upload", "Home", FormMethod.Post,
    new
    {
        enctype = "multipart/form-data",
        id = "ImgForm",
        name = "ImgForm",
        target = "UploadTarget"
    }))
    {
        
@* DataBase Record ID as Hidden Field against which we are uplaoding file  *@
         
        @Html.Hidden("ID", 65512)
        <div style="width: 100px; margin: auto; float: left; line-height: 30px; font-weight: bold;">File</div>
             
        <div style="width: 200px; float: left; margin: auto;">

            <input type="file" id="uploadFile" name="file" data-val="true" data-val-required="please select a file" />
            <span id="validityMessages" style="color: Red;"></span>
        </div>
      
        <div style="clear: both;"></div>
     
        <div style="width: auto; margin-top: 3%;">
            <div style="margin: 5% 18%;">
                <input id="upload" type="button" value="Upload" name="Upload" onclick="UploadImage()" />
                <span id="uploadLoader" style="display: none;">
                    <img id="searchLoader" src="@Url.Content("~/Content/Images/loader.gif")" />Uploading Please Wait</span>
            </div>
        </div>
    
    
    }
    <div id="uploadsContainer"></div>
    <iframe id="UploadTarget" name="UploadTarget" onload="UploadImage_Complete();" style="position: absolute; left: -999em; top: -999em;"></iframe>
</div>
@section Scripts{
   <script type="text/javascript">
        $(document).ready(function () {
            $('img.delUpload').live('click', function () {
                var Id = this.id;
                var url = '@Url.Action("DeleteFile","Home")';

                url = url + "?id=" + Id
                $.get(url, function (response) {

                   $('#uploadsContainer').html(response);
                });
           });
        });
    </script>
    <script type="text/javascript">
        var isFirstLoad = true;
        function UploadImage() {
            // check for size of file not greater than 1MB
            if ($("#uploadFile").val()) {

                var iSize = ($("#uploadFile")[0].files[0].size / 1024);

                iSize = (Math.round((iSize / 1024) * 100) / 100);
                if (iSize > 4) {
                    alert("File must be less than 4MB");

                    $('span#validityMessages').html("File must be less than 4MB");
                   return;
                }
                else {
                    // on form post showing Busy Indicator
                    $('#uploadLoader').show();
                    $("#ImgForm").submit(); // post form
                    console.log(iSize + "Mb");
                }
            }

                // check for no file selected for upload

            else {

                $('span#validityMessages').html("Please select a File of size less than 4MB!");
                return;
            }
        }

        function UploadImage_Complete() {
            //Check to see if this is the first load of the iFrame
            if (isFirstLoad == true) {
                isFirstLoad = false;
            }

            $('#uploadLoader').hide();
            //Reset the image form so the file won't get uploaded again
            document.getElementById("ImgForm").reset();

            // this call will get uploads if any exists on server against this id and after successfull upload refreshing partial view to get the latest uploads
            GetFiles();
        }
        function GetFiles() {
            var url = '@Url.Action("GetFiles","Home")';
            var Id = $('input#ID').val();
            url = url + "?id=" + Id
            $.get(url, function (response) {
               $('#uploadsContainer').html(response);
            });
        }

    </script>
}

Uploads ViewModel

در کد زیر Uploads ViewModel که یک View از نوع Strongly Type است نشان داده شده است .در این view از partial view به نام uploads هم استفاده شده است .

public class UploadsViewModel
    {
        public long ID { get; set; }
        public List<file> Uploads { get; set; }

        public UploadsViewModel()
        {
            this.Uploads = new List<file>();
        }
    }


    public class File
    {
        public string FilePath { get; set; }
        public long FileID { get; set; }
        public string FileName { get; set; }
    }
</file></file>

متد کنترلر مربوط به upload

در زیر متد Upload آورده شده است .این متد دو پارامتر دارد .یکی از نوع    و دیگری از نوع FormCollection  

HttpPostedFileBase  که پروتکل Http فایل هایی که از طریق آن ارسال می شود را به صورت  view در یافت می کند. دراینHttpPostedFileBase  یک سری فیلد های مخفی را ابتدا پر کرده و سپس در DB ذخیره می کنیم و در نهایت یک Partial View بر می گردانیم.

همان طور  که می بینید از Session برای نگه داری موقت اطلاعات مربوط به State استفاده شده است .

 

 

[HttpPost]
public ActionResult Upload(FormCollection form, HttpPostedFileBase file)
{

    UploadsViewModel uploadsViewModel = Session["Uploads"] != null ? Session["Uploads"] as UploadsViewModel : new UploadsViewModel();


    uploadsViewModel.ID = long.Parse(form["id"]);

    File upload = new File();
    upload.FileID = uploadsViewModel.Uploads.Count + 1;
    upload.FileName = file.FileName;
    upload.FilePath = "~/Uploads/" + file.FileName;



    //if (file.ContentLength < 4048576)    we can check file size before saving if we need to restrict file size or we can check it on client side as well
    //{

        if (file != null)
        {
            file.SaveAs(Server.MapPath(upload.FilePath));
            uploadsViewModel.Uploads.Add(upload);
            Session["Uploads"] = uploadsViewModel;
        }

        // Save FileName and Path to Database according to your business requirements

    //}


    return PartialView("~/Views/Shared/_UploadsPartial.cshtml", uploadsViewModel.Uploads);
}

Partial View مربوط به Uploads

کد مربوط به این View به صورت زیر است .

@model List<AjaxFileUploading.ViewModels.File>
@{
    Layout = null;
    var IsAnyImage = Model.Any(x => new String[] { "jpg", "jpeg", "png", "gif" }.Contains(x.FileName.Split('.').Last()));

}


@if (Model.Any())
{
    <h3>Uploads</h3>
    <hr />
    <table style="width: 500px;">
        <tr>
            @if (IsAnyImage)
            {
                <th>Thumbnail</th>
            }
            <th>FileName</th>
            <th>Action</th>
        </tr>
        <tbody>
            @for(int i=0; i< Model.Count; i++)
            {
                <tr>

                    
                        <td style="text-align: center;">
                            <img width="60" src="@Url.Content(Model[i].FilePath)" />

                        </td>
                    
                    

                    <td style="text-align: center;">
                        <a href="@Url.Content("~/Uploads/" + Model[i].FileName)" target="_blank">@Model[i].FileName</a></td>
                    <td style="text-align: center;">
                        <img id="@Model[i].FileID" class="delUpload" width="20" src="@Url.Content("~/Content/Images/delete.png")" /></td>
                </tr>
            }
        </tbody>
    </table>

}
else
{
    <h3>No Uploads Found!</h3>
}

Javascript و Jquery مربوط به View اصلی

اولین چیزی که به آن احتیاج داریم از آنجایی که MVC کار می کنیم این اسکریپت ها را در داخل Bundle ها مدیریت می کنیم .در داخل Layout کد زیر را می نویسیم

@Scripts.Render("~/bundles/jquery")

حال نوبت این است که فرم در داخل iFrame لود شود.در زیر کدی که مسئول چک کردن این است که آیا اصلا فایلی وجود دارد یا نه و اینکه این فایل آیا Valid است یا نه .زمانی که iframe لود می شود این رویداد یک تابعی را فراخوانی می کند.بار اول هم که این Iframe لود می شود هم یک تابع را فراخوانی می کند در این صورت برای اینکه Validation ایراد نداشته باشد از یک Flag برای Validation استفاده کرده ایم .

<script type="text/javascript">
        var isFirstLoad = true;
        function UploadImage() {
            // check for size of file not greater than 1MB
            if ($("#uploadFile").val()) {
                var iSize = ($("#uploadFile")[0].files[0].size / 1024);
                iSize = (Math.round((iSize / 1024) * 100) / 100);
                if (iSize > 4) {
                    alert("File must be less than 4MB");
                    $('span#validityMessages').html("File must be less than 4MB");
                    return;
                }
                else {
                    // on form post showing Busy Indicator
                    $('#uploadLoader').show();

                    console.log(iSize + "Mb");
               }
            }
                // check for no file selected for upload

            else {
                $('span#validityMessages').html("Please select a File of size less than 4MB!");
                return;
            }
       }

        function UploadImage_Complete() {
            //Check to see if this is the first load of the iFrame
            if (isFirstLoad == true) {
               isFirstLoad = false; 
            }
            $('#uploadLoader').hide();
            //Reset the image form so the file won't get uploaded again
            document.getElementById("ImgForm").reset();
            // this call will get uploads if any exists on server against this id and after successfull upload refreshing partial view to get the latest uploads
            GetFiles();
        }
</script>

متد مربوط به Delete

در زیر کد مربوط به حذف فایل ها آورده شده است .

public ActionResult DeleteFile(long id)
{

    /* Use input Id to delete the record from db logically by setting IsDeleted bit in your table or delete it physically whatever is suitable for you
       for DEMO purpose i am stroing it in Session */

    UploadsViewModel viewModel = Session["Uploads"] as UploadsViewModel;

    File file = viewModel.Uploads.Single(x => x.FileID == id);

    try
    {
        System.IO.File.Delete(Server.MapPath(file.FilePath));
       viewModel.Uploads.Remove(file);
    }
    catch (Exception)
    {
        return PartialView("~/Views/Shared/_UploadsPartial.cshtml", viewModel.Uploads);
    }
    return PartialView("~/Views/Shared/_UploadsPartial.cshtml", viewModel.Uploads);
}
فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید