jQuery Ajax File Upload

Viewed 1717196

Can I use the following jQuery code to perform file upload using POST method of an ajax request ?

$.ajax({
    type: "POST",
    timeout: 50000,
    url: url,
    data: dataString,
    success: function (data) {
        alert('success');
        return false;
    }
});

If it is possible, do I need to fill data part? Is it the correct way? I only POST the file to the server side.

I have been googling around, but what I found was a plugin while in my plan I do not want to use it. At least for the moment.

27 Answers

File upload is not possible through AJAX.
You can upload file, without refreshing page by using IFrame.
You can check further details here.


UPDATE

With XHR2, File upload through AJAX is supported. E.g. through FormData object, but unfortunately it is not supported by all/old browsers.

FormData support starts from following desktop browsers versions.

  • IE 10+
  • Firefox 4.0+
  • Chrome 7+
  • Safari 5+
  • Opera 12+

For more detail, see MDN link.

Here's how I got this working:

HTML

<input type="file" id="file">
<button id='process-file-button'>Process</button>

JS

$('#process-file-button').on('click', function (e) {
    let files = new FormData(), // you can consider this as 'data bag'
        url = 'yourUrl';

    files.append('fileName', $('#file')[0].files[0]); // append selected file to the bag named 'file'

    $.ajax({
        type: 'post',
        url: url,
        processData: false,
        contentType: false,
        data: files,
        success: function (response) {
            console.log(response);
        },
        error: function (err) {
            console.log(err);
        }
    });
});

PHP

if (isset($_FILES) && !empty($_FILES)) {
    $file = $_FILES['fileName'];
    $name = $file['name'];
    $path = $file['tmp_name'];


    // process your file

}

Using pure js it is easier

async function saveFile(inp) 
{
    let formData = new FormData();           
    formData.append("file", inp.files[0]);
    await fetch('/upload/somedata', {method: "POST", body: formData});    
    alert('success');
}
<input type="file" onchange="saveFile(this)" >

  • In server side you can read original file name (and other info) which is automatically included to request.
  • You do NOT need to set header "Content-Type" to "multipart/form-data" browser will set it automatically
  • This solutions should work on all major browsers.

Here is more developed snippet with error handling, timeout and additional json sending

async function saveFile(inp) 
{
    let user = { name:'john', age:34 };
    let formData = new FormData();
    let photo = inp.files[0];      
         
    formData.append("photo", photo);
    formData.append("user", JSON.stringify(user));  
    
    const ctrl = new AbortController() // timeout
    setTimeout(() => ctrl.abort(), 50000);

    try {
       let r = await fetch('/upload/image', 
         {method: "POST", body: formData, signal: ctrl.signal}); 
       console.log('HTTP response code:',r.status); 
       alert('success');
    } catch(e) {
       console.log('Huston we have problem...:', e);
    }
    
}
<input type="file" onchange="saveFile(this)" >
<br><br>
Before selecting the file Open chrome console > network tab to see the request details.
<br><br>
<small>Because in this example we send request to https://stacksnippets.net/upload/image the response code will be 404 ofcourse...</small>

Use FormData. It works really well :-) ...

var jform = new FormData();
jform.append('user',$('#user').val());
jform.append('image',$('#image').get(0).files[0]); // Here's the important bit

$.ajax({
    url: '/your-form-processing-page-url-here',
    type: 'POST',
    data: jform,
    dataType: 'json',
    mimeType: 'multipart/form-data', // this too
    contentType: false,
    cache: false,
    processData: false,
    success: function(data, status, jqXHR){
        alert('Hooray! All is well.');
        console.log(data);
        console.log(status);
        console.log(jqXHR);

    },
    error: function(jqXHR,status,error){
        // Hopefully we should never reach here
        console.log(jqXHR);
        console.log(status);
        console.log(error);
    }
});

2019 update:

html

<form class="fr" method='POST' enctype="multipart/form-data"> {% csrf_token %}
<textarea name='text'>
<input name='example_image'>
<button type="submit">
</form>

js

$(document).on('submit', '.fr', function(){

    $.ajax({ 
        type: 'post', 
        url: url, <--- you insert proper URL path to call your views.py function here.
        enctype: 'multipart/form-data',
        processData: false,
        contentType: false,
        data: new FormData(this) ,
        success: function(data) {
             console.log(data);
        }
        });
        return false;

    });

views.py

form = ThisForm(request.POST, request.FILES)

if form.is_valid():
    text = form.cleaned_data.get("text")
    example_image = request.FILES['example_image']

Using FormData is the way to go as indicated by many answers. here is a bit of code that works great for this purpose. I also agree with the comment of nesting ajax blocks to complete complex circumstances. By including e.PreventDefault(); in my experience makes the code more cross browser compatible.

    $('#UploadB1').click(function(e){        
    e.preventDefault();

    if (!fileupload.valid()) {
        return false;            
    }

    var myformData = new FormData();        
    myformData.append('file', $('#uploadFile')[0].files[0]);

    $("#UpdateMessage5").html("Uploading file ....");
    $("#UpdateMessage5").css("background","url(../include/images/loaderIcon.gif) no-repeat right");

    myformData.append('mode', 'fileUpload');
    myformData.append('myid', $('#myid').val());
    myformData.append('type', $('#fileType').val());
    //formData.append('myfile', file, file.name); 

    $.ajax({
        url: 'include/fetch.php',
        method: 'post',
        processData: false,
        contentType: false,
        cache: false,
        data: myformData,
        enctype: 'multipart/form-data',
        success: function(response){
            $("#UpdateMessage5").html(response); //.delay(2000).hide(1); 
            $("#UpdateMessage5").css("background","");

            console.log("file successfully submitted");
        },error: function(){
            console.log("not okay");
        }
    });
});

To get all your form inputs, including the type="file" you need to use FormData object. you will be able to see the formData content in the debugger -> network ->Headers after you will submit the form.

var url = "YOUR_URL";

var form = $('#YOUR_FORM_ID')[0];
var formData = new FormData(form);


$.ajax(url, {
    method: 'post',
    processData: false,
    contentType: false,
    data: formData
}).done(function(data){
    if (data.success){ 
        alert("Files uploaded");
    } else {
        alert("Error while uploading the files");
    }
}).fail(function(data){
    console.log(data);
    alert("Error while uploading the files");
});
<input class="form-control cu-b-border" type="file" id="formFile">
<img id="myImg" src="#">

In js

<script>
    var formData = new FormData();
    formData.append('file', $('#formFile')[0].files[0]);
    $.ajax({
        type: "POST",
        url: '/GetData/UploadImage',
        data: formData,
        processData: false, // tell jQuery not to process the data
        contentType: false, // tell jQuery not to set contentType
        success: function (data) {
            console.log(data);
            $('#myImg').attr('src', data);
        },
        error: function (xhr, ajaxOptions, thrownError) {
        }
    })
</script>

In controller

public ActionResult UploadImage(HttpPostedFileBase file)
        {
            string filePath = "";
            if (file != null)
            {
                string path = "/uploads/Temp/";
                if (!Directory.Exists(Server.MapPath("~" + path)))
                {
                    Directory.CreateDirectory(Server.MapPath("~" + path));
                }
                filePath = FileUpload.SaveUploadedFile(file, path);
            }
            
            return Json(filePath, JsonRequestBehavior.AllowGet);
        }

$("#form-id").submit(function (e) {
   e.preventDefault();
});

$("#form-id").submit(function (e) {

     var formObj = $(this);
     var formURL = formObj.attr("action");
     var formData = new FormData(this);
          $.ajax({
             url: formURL,
             type: 'POST',
             data: formData,
             processData: false,
             contentType: false,
             async: true,
             cache: false,
             enctype: "multipart/form-data",
             dataType: "json",
             success: function (data) {
                 if (data.success) {
                        alert(data.success)
                 } 
                                
                 if  (data.error) {
                        alert(data.error)
                 } 
             }
         });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

<form class="form-horizontal" id="form-id" action="masterFileController" enctype="multipart/form-data">
    <button class="btn-success btn" type="submit" id="btn-save" >Submit</button>
</form>

servlet responce as "out.print("your responce");"

This is my code that it works

var formData = new FormData();
var files = $('input[type=file]');
for (var i = 0; i < files.length; i++) {
if (files[i].value == "" || files[i].value == null) {
 return false;
}
else {
 formData.append(files[i].name, files[i].files[0]);
}
}
var formSerializeArray = $("#Form").serializeArray();
for (var i = 0; i < formSerializeArray.length; i++) {
  formData.append(formSerializeArray[i].name, formSerializeArray[i].value)
}
$.ajax({
 type: 'POST',
 data: formData,
 contentType: false,
 processData: false,
 cache: false,
 url: '/Controller/Action',
 success: function (response) {
 if (response.Success == true) {
    return true;
 }
 else {
    return false;
 }
 },
 error: function () {
   return false;
 },
 failure: function () {
   return false;
 }
 });
Related