Search code examples
javascriptc#ajaxmodel-view-controller.net-6.0

Upload file using Ajax MVC .NET 6


I need to upload an image (single) to the server using Ajax. Action will call the post Method on my API controller.

Up to this point, I found only outdated and broken solution like this :

https://www.c-sharpcorner.com/UploadFile/manas1/upload-files-through-jquery-ajax-in-Asp-Net-mvc/

or this : Uploading File using Ajax in Asp.Net Core

Also, it was surprising that example from Microsoft documentation not working either in my case, https://learn.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-2.1#match-name-attribute-value-to-parameter-name-of-post-method

List<IFormFile> files was null, I don't know why.

My Current code :

HTML

<form id="InputBannerForm"  action="@Url.Action("UploadLocalBanner", "Api")" enctype="multipart/form-data" onsubmit="AJAXSubmit(this);return false;" method="post" >
                            <div class="row justify-content-center">
                                <div class="col-12 col-md-6 mb-3">
                            <div>
                            <label for="InputBanner" class="col-12"  >@Localizer["banner"]</label>
                            <input id="InputBanner" type="file" accept="image/jpeg, image/png" onchange="onInputBannerChange()">
                      </div>
                </div>
           </div>
    </form>

JS :

function onInputBannerChange() {
    document.getElementById("InputBannerForm").submit();
}

async function AJAXSubmit(oFormElement) {
    var resultElement = oFormElement.elements.namedItem("result");
    const formData = new FormData(oFormElement);

    try {
        const response = await fetch(oFormElement.action, {
            method: 'POST',
            body: formData
        });

        if (response.ok) { console.log("succes"); }

        resultElement.value = 'Result: ' + response.status + ' ' +
            response.statusText;
    } catch (error) {
        console.error('Error:', error);
    }
}

Controller :

[Route("SO-{clientId}/{controller}/{action}")]
[HttpPost]
public async Task<IActionResult> UploadLocalBanner(IList<IFormFile> files)
{
    try
    {
        IFormFile source = files[0];
        string filename = System.Net.Http.Headers.ContentDispositionHeaderValue.Parse(source.ContentDisposition).FileName.Trim('"');

        if (filename.Contains("\\"))
            filename = filename.Substring(filename.LastIndexOf("\\") + 1);

        string relativePath = "wwwroot\\Images\\LocalBanners";

        string targetDirectory = System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), relativePath);
        if (!System.IO.Directory.Exists(targetDirectory))
            System.IO.Directory.CreateDirectory(targetDirectory);

        using (System.IO.FileStream output = System.IO.File.Create(System.IO.Path.Combine(targetDirectory, filename)))
        {
            await source.CopyToAsync(output);
        }
    
        return Ok();
    }
    catch(Exception ex)
    {
        return BadRequest(ex);
    }
}

Solution

  • I manage to achieve my goal with this code :

        <div id="TableHeader" class="row header">
            <h3 >@Localizer["_Local Banners List_ : "]</h3>
            <input type="file" id="imageUploadForm"  name="image" multiple="multiple" />
        </div>
    

    This is my Ajax request :

    $(document).ready(function() {
        $("#imageUploadForm").change(function() {
            var formData = new FormData();
            var totalFiles = document.getElementById("imageUploadForm").files.length;
            for (var i = 0; i < totalFiles; i++) {
                var file = document.getElementById("imageUploadForm").files[i];
                formData.append("imageUploadForm", file);
            }
            formData.append("localId", @Model.LocalId);
    
            $.ajax({
                type: "POST",
                url: _UploadLocalBannerImage,
                data: formData,
                dataType: 'json',
                contentType: false,
                processData: false
            }).done(function() { alert('success'); }).fail(function(xhr, status, errorThrown) { alert('fail'); });
        });
    });
    

    And this is how I handle request on server :

        [HttpPost]
        public IActionResult UploadasdaLocalBannerImage(int localId)
        {
    
            if (Request.Form.Files.Count != 0)
            {
                for (int i = 0; i < Request.Form.Files.Count; i++)
                {
                    var file = Request.Form.Files[i];
                    // var fileName = System.IO.Path.GetFileName(file.FileName);
    
                    // some logic before aploading file and prepearing
                    
                    string absolutePath = System.IO.Path.Combine(Environment.CurrentDirectory, "path to file");
                    using (var stream = new System.IO.FileStream(absolutePath, System.IO.FileMode.Create))
                    {
                        file.CopyTo(stream);
                    }
    
                    // some logic after file uploaded
    
                    return Ok();
                    //file.SaveAs(path);
                }
    
            }
            return BadRequest();
    
        }