1

I have been searching online looking for the answer to this problem but I cannot seem to find anything that works, I have the following Controller code:

[HttpPost]
public ActionResult UploadFiles()
{
    // If files exist
    if (Request.Files != null && Request.Files.Count > 0)
    {
        // ** Do stuff

        return Json(new { result = true, responseText = "File(s) uploaded successfully" });
    }

    // Return no files selected
    return Json(new { result = false, responseText = "No files selected" });
}

And following code in my cshtml page which works fine and the controller can see the files that I upload:

<input type="file" name="files" id="files" accept="image/*;capture=camera" multiple>
<button type="button" onclick="submitform()">Submit</button>

<script>
    function submitform(){

        // Get files from upload
        var files = $("#files").get(0).files;

        // Create form data object
        var fileData = new FormData();

        // Loop over all files and add it to FormData object
        for (var i = 0; i < files.length; i++) {
            fileData.append(files[i].name, files[i]);
        }

        // Send files to controller
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "/Quotes/QuoteFiles/UploadFiles", false);
        xhr.send(fileData);     
    }

</script>

However when I try and change this to work using an Ajax call as shown below then Request.Files in the Controller always has no files. The only bit I have changed is the "Send files to controller" part:

<input type="file" name="files" id="files" accept="image/*;capture=camera" multiple>
<button type="button" onclick="submitform()">Submit</button>

<script>
    function submitform(){

        // Get files from upload
        var files = $("#files").get(0).files;

        // Create form data object
        var fileData = new FormData();

        // Loop over all files and add it to FormData object
        for (var i = 0; i < files.length; i++) {
            fileData.append(files[i].name, files[i]);
        }

        // Send files to controller
        $.ajax({
            url: '/Quotes/QuoteFiles/UploadFiles',
            type: "POST",
            contentType: false, // Not to set any content header
            processData: false, // Not to process data
            data:  fileData,
            success: function (result) {
                alert(result);
            },
            error: function (err) {
                alert(err.statusText);
            }
        });

    }

</script>

I am running this in Google Chrome but I have tried IE 11 and Edge but not of them work. Can anyone tell me what I am doing wrong?

WebFletch
  • 172
  • 1
  • 16
  • Are there any error messages logged at `console`? What do return a string as a `JSON` response? – guest271314 Dec 14 '16 at 22:53
  • i dont think you can upload files with ajax, you can do it with an iframe to simulate the effect – Abdul Hamid Dec 14 '16 at 22:58
  • 1
    @AbdulHamid Files can be uploaded using `$.ajax()` – guest271314 Dec 14 '16 at 23:02
  • i just found out that now it is possible to do this in the newest browsers – Abdul Hamid Dec 14 '16 at 23:03
  • @AbdulHamid https://jsfiddle.net/xa7Lq5uj/ – guest271314 Dec 14 '16 at 23:04
  • As mentioned above you can upload using Jquery, there are various articles online which I have tried following but cannot get it to work. @guest271314 there are no console errors and are you asking me why I am returning a JSON string? – WebFletch Dec 15 '16 at 00:11
  • @trletch _"are you asking me why I am returning a JSON string?"_ Yes. Why do you not return a string, instead of `JSON`? For example, `JSON.stringify("abc")` returns string surrounding `"abc"` with double quotes `""abc""`. Note, have not tried `asp.net-mvc`. Is `int quoteId` retrieved from `POST` request query string? Is `quoteId` an integer or a string within `asp.net-mvc`? Expected result is returned at jsfiddle. – guest271314 Dec 15 '16 at 00:16
  • @trfletch See `Network` tab at https://jsfiddle.net/xa7Lq5uj/3/ after clicking `Submit` – guest271314 Dec 15 '16 at 00:29
  • @guest271314 I am just testing at the moment so I haven't sorted out what is going to be returned but it will be JSON because I will be returning a number of things not just a success message, also the quoteId is irrelevant to my question (along with what I am returning), maybe I should not have included those parts in my example if it is causing confusion. The important part is Request.Files which has files when doing it the first way but does not when doing it using the Ajax call. – WebFletch Dec 16 '16 at 13:29
  • @trfletch Have not tried `asp.net-mvc`. Cannot offer a tried and tested solution to that portion of Question, here. Does `int quoteId` reference the query string portion of request? Is `quoteId` expected to be an integer? – guest271314 Dec 16 '16 at 20:26
  • @guest271314 I tried the changes in your jsfiddle but they made no difference, the only relevant changes I could see were you included "(result, textStatus, jqxhr)" in the success and error functions and you removed "cache: false". I have edited my original question to remove the references to "quoteId" and to return JSON rather than just a string because as mentioned above these parts are not relevant to my question. The issue is that Request.Files is empty when using an ajax call but not empty when using an XMLHttpRequest call. – WebFletch Dec 19 '16 at 23:12
  • Have not tried `asp.net-mvc`. Have you tried sending only `File` object? What is `Request` at server? One difference is `async` being set to `false` at `XMLHttpRequest`? – guest271314 Dec 19 '16 at 23:17
  • What is purpose of using jQuery where `XMLHttpRequest` returns expected result? – guest271314 Dec 19 '16 at 23:24
  • @guest271314 I want to use Jquery so that I can show the progress of the files that are being uploaded, run validation on them before they are uploaded and as per the following page it isn't recommended to upload files synchronously http://stackoverflow.com/questions/27736186/jquery-has-deprecated-synchronous-xmlhttprequest – WebFletch Dec 21 '16 at 13:14
  • @trfletch _"I want to use Jquery so that I can show the progress of the files that are being uploaded"_ Not sure how jQuery is related to showing progress of uploaded file? You can use `XMLHttpRequestUpload.prototype.onprogress` to display progress of uploaded files. What do you mean by "run validation on them"? Though the process would require more bytes to upload each file, you could send the uploaded file as a `data URI` [Upload multiple image using AJAX, PHP and jQuery](http://stackoverflow.com/q/28856729/) – guest271314 Dec 22 '16 at 00:21
  • My end goal is to use the following plugin https://blueimp.github.io/jQuery-File-Upload/ so that I can show progress and check the file types (i.e. images, pdf's etc) and check the file size before they are uploaded but I am unable to get the basics of uploading using jQuery working let alone using the jQuery-File-Upload plugin. – WebFletch Jan 04 '17 at 14:48

2 Answers2

0

try using a fileReader instead of a formData and change the mimetype to 'text/plain; charset=x-user-defined-binary'

https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications#Example_Uploading_a_user-selected_file

Abdul Hamid
  • 168
  • 1
  • 7
  • 25
0

I have finally found what was causing this issue, I have the following code on my _Layout.cshtml page which is there to automatically send the AntiForgeryToken on any ajax requests I make, this appears to be causing the problem because once I remove it Request.Files is not empty. I now need to see if I can find a way to add this code back in where it will not stop file uploads working:

$(document).ready(function () {
    var securityToken = $('[name=__RequestVerificationToken]').val();
    $(document).ajaxSend(function (event, request, opt) {
        if (opt.hasContent && securityToken) {   // handle all verbs with content
            var tokenParam = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
            opt.data = opt.data ? [opt.data, tokenParam].join("&") : tokenParam;
            // ensure Content-Type header is present!
            if (opt.contentType !== false || event.contentType) {
                request.setRequestHeader("Content-Type", opt.contentType);
            }
        }
    });
});

**** EDIT ****

I have now reworked this as shown below to add 'if(opt.data != "[object FormData]"' which resolves the issue by not calling the code if it is a file upload:

$(document).ready(function () {
    var securityToken = $('[name=__RequestVerificationToken]').val();
    $(document).ajaxSend(function (event, request, opt) {
        if (opt.hasContent && securityToken) {   // handle all verbs with content
            // If not "FormData" (i.e. not a file upload)
            if (opt.data != "[object FormData]")
            {
                 var tokenParam = "__RequestVerificationToken=" + encodeURIComponent(securityToken);
                opt.data = opt.data ? [opt.data, tokenParam].join("&") : tokenParam;
                // ensure Content-Type header is present!
                if (opt.contentType !== false || event.contentType) {
                    request.setRequestHeader("Content-Type", opt.contentType);
                }
             }

         }
     });
 });
WebFletch
  • 172
  • 1
  • 16