14

I am working with ASP.Net Core 2.1, and trying to upload a file while returning it's url, without refreshing the page.

I am trying to write the JavaScript in site.js as the _RenderPartial("scripts") renders all scripts at the end of the page and hence directly using script tag in the razor view is not working. Secondly, adding it to site.js gives me an opportunity to call the script across the site views.

My Controller action looks like :

    [HttpPost]
    [DisableRequestSizeLimit]
    public async Task<IActionResult> Upload()
    {
      // Read & copy to stream the content of MultiPart-Form
      // Return the URL of the uploaded file
      return Content(FileName);
    }

My view looks like :

<form id="FileUploadForm" action="~/Resources/Upload" method="post" enctype="multipart/form-data">
<input name="uploadfile" type="file" />
<button name="uploadbtn" type="submit" onclick="SubmitForm(this.parentElement, event)">Upload</button>

The site.js currently looks like :

function SubmitForm(form, caller) {
caller.preventDefault();
$.ajax(
    {
        type: form.method,
        url: form.action,
        data: form.serialize(),
        success: function (data) { alert(data); },
        error: function (data) { alert(data); }
    })}

Presently, the code bypasses the entire script and the file is uploaded and new view displaying the file name is returned. I need help to create the javascript.

2

2 Answers 2

13

Unfortunately the jQuery serialize() method will not include input file elements. So the file user selected is not going to be included in the serialized value (which is basically a string).

What you may do is, create a FormData object, append the file(s) to that. When making the ajax call, you need to specify processData and contentType property values to false

<form id="FileUploadForm" asp-action="Upload" asp-controller="Home" 
                                              method="post" enctype="multipart/form-data">
    <input id="uploadfile" type="file" />
    <button name="uploadbtn" type="submit">Upload</button>
</form>

and here in the unobutrusive way to handle the form submit event where we will stop the regular behavior and do an ajax submit instead.

$(function () {
    $("#FileUploadForm").submit(function (e) {
        e.preventDefault();

        console.log('Doing ajax submit');

        var formAction = $(this).attr("action");
        var fdata = new FormData();

        var fileInput = $('#uploadfile')[0];
        var file = fileInput.files[0];
        fdata.append("file", file);

        $.ajax({
            type: 'post',
            url: formAction,
            data: fdata,
            processData: false,
            contentType: false
        }).done(function (result) {
            // do something with the result now
            console.log(result);
            if (result.status === "success") {
                alert(result.url);
            } else {
                alert(result.message);
            }
        });
    });
})

Assuming your server side method has a parameter of with name same as the one we used when we created the FormData object entry(file). Here is a sample where it will upload the image to the uploads directory inside wwwwroot.

The action method returns a JSON object with a status and url/message property and you can use that in the success/done handler of the ajax call to whatever you want to do.

public class HomeController : Controller
{
    private readonly IHostingEnvironment hostingEnvironment;
    public HomeController(IHostingEnvironment environment)
    {
        _context = context;
        hostingEnvironment = environment;
    }
    [HttpPost]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        try
        {
            var uniqueFileName = GetUniqueFileName(file.FileName);
            var uploads = Path.Combine(hostingEnvironment.WebRootPath, "uploads");
            var filePath = Path.Combine(uploads, uniqueFileName);
            file.CopyTo(new FileStream(filePath, FileMode.Create));
            var url = Url.Content("~/uploads/" + uniqueFileName);
            return Json(new { status = "success", url = url });
        }
        catch(Exception ex)
        {
            // to do : log error
            return Json(new { status = "error", message = ex.Message });
        }
    }
    private string GetUniqueFileName(string fileName)
    {
        fileName = Path.GetFileName(fileName);
        return Path.GetFileNameWithoutExtension(fileName)
                  + "_"
                  + Guid.NewGuid().ToString().Substring(0, 4)
                  + Path.GetExtension(fileName);
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

The selector $('#uploadfile')[0] returns null. To make this function usable across the website, i am passing an instance of the form. How can i get all the file input elements of the form ?
Referring to developer.mozilla.org/en-US/docs/Web/API/FormData/FormData : The formData doesn't seems to be fully implemented on all the browsers. What can be the alternatives ?
If $('#uploadfile')[0] returns null, that means your file element does not have an Id uploadfile (check the markup in answer). Update your jQuery selector to match with your markup. If you want to send more than one file, use a more generic jQuery selector (Ex : name / class) and use a loop to add that to the FormData object. Update your sever side code to accept a collection of IFormFile. Which specific browser is not supporting it ?
Thanks @Shyju ! Finally got it running, posting my code as well for reference. Just a query, i tried passing the form as a parameter in the FormData constructor, but seems like that doesn't work. I referred this article : developer.mozilla.org/en-US/docs/Web/API/FormData/…
Regarding browser support, the mozilla api documentation says the compatibility status is unknown for iOS Safari and Samsung Internet. Which makes me believe that the implementation of FormData api might be work in progress.
1

Sharing the code that worked for me, implementing @Shyju's answer.

View ( Razor Page ):

<form name="UploadForm" action="~/Resources/Upload" method="post" enctype="multipart/form-data">
<input name="uploadfile" type="file" />
<button name="uploadbtn" type="submit" onclick="SubmitForm(this.parentElement, event)">Upload</button>

AJAX code added in Site.js (to make it a reusable):

// The function takes Form and the event object as parameter
function SubmitForm(frm, caller) {
caller.preventDefault();

var fdata = new FormData();

var file = $(frm).find('input:file[name="uploadfile"]')[0].files[0];
fdata.append("file", file);

$.ajax(
    {
        type: frm.method,
        url: frm.action,
        data: fdata, 
        processData: false,
        contentType: false,
        success: function (data) {
            alert(data);
        },
        error: function (data) {
            alert(data);
        }
    })

};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.