AJAX File Upload with XMLHttpRequest

23,965

Solution 1

To avoid the post_max_size limitation problem... but also out of memory problems on both sides :

On the client side

  • use PUT instead of POST :

    xhr.open("put", "upload.php", true);

  • add custom headers to specify original FileName and FileSize :

    xhr.setRequestHeader("X-File-Name", file.name);
    xhr.setRequestHeader("X-File-Size", file.size);

  • use the File object directly in your XHR send method :

    xhr.send(file);

    Please note that the File object can be obtained directly via the “files” property of your input[type=file] DOM object

On the server side

  • read the custom headers via $_SERVER :

    $filename = $_SERVER['HTTP_X_FILE_NAME'];
    $filesize = $_SERVER['HTTP_X_FILE_SIZE'];

  • read file data using php://input :

    $in = fopen('php://input','r');

You'll then be able to send very big files (1GB or more) without any limitation!!!

This code works for FireFox 4+, Chrome 6+, IE10+

Solution 2

Change the post_max_size directive in the ini file

Solution 3

The Ajax call will not limit the size. It is probably the max file size in the php ini file.

Share:
23,965

Related videos on Youtube

Danilo Valente
Author by

Danilo Valente

GitHub account: Danilo Valente NPM profile: danilo-valente

Updated on December 25, 2020

Comments

  • Danilo Valente
    Danilo Valente over 3 years

    I know there are a lot of similar questions, but I still haven't found a solution for my problem. I'm trying to upload a file with XMLHttpRequest, so I developed the code below:

    var sendFiles = function(url,onload,onerror,file,headers){
        var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHttp'),
        upload = xhr.upload;
        API.addEvent(xhr,'readystatechange',function(){
            if(xhr.readyState==4)
                if((xhr.status>=200 && xhr.status<300) || xhr.status==304){
                    this.response = this.response || this.responseText;
                    onload.call(xhr);
                }else onerror.call(xhr);
        });
        xhr.open('POST',url,true);
        for(var n=0;n<headers.length;n++)
            xhr.setRequestHeader(headers[n]);
        xhr.send(file);
        return xhr;
    };
    

    And the PHP-side script is:

    <?php
    header('Content-type: text/html;charset=ISO-8859-1');
    $status = 0;
    if(@copy($_FILES['file']['tmp_name'],'test\\' . $_FILES['file']['name']))
        $status = 1;
    else
        $err = '0';
    echo '{
        "status": ' . $status . '
    }';
    ?>;
    

    But the var $_FILES['file'] seems to be empty, which means that the file isn't being sent to the server. Then i decided to use the FormData Object, in the code below

    var sendFiles = function(url,onload,onerror,file,headers){
        var xhr = XMLHttpRequest ? new XMLHttpRequest() : new    ActiveXObject('Microsoft.XMLHttp'),
        upload = xhr.upload,
        formData = new FormData();
        formData.append('file',file);
        API.addEvent(xhr,'readystatechange',function(){
            if(xhr.readyState==4)
                if((xhr.status>=200 && xhr.status<300) || xhr.status==304){
                    this.response = this.response || this.responseText;
                    onload.call(xhr);
                }else onerror.call(xhr);
        });
        xhr.open('POST',url,true);
        for(var n=0;n<headers.length;n++)
            xhr.setRequestHeader(headers[n]);
        xhr.send(formData);
        return xhr;
    };
    

    And it worked, but only with file sizes low to about 8mb. When I try sending a file that has more than 8mb of size, the var $_FILES['file'] becomes empty again

    NOTE: the 'file' var corresponds to something like document.getElementsById('fileInput').files[0];

    • epascarello
      epascarello about 12 years
      And what is the upload file size limit in your ini file?
  • Green
    Green over 11 years
    Good explanation, thank you, but there are two limitations in your solution. 1st: If I use the File object directly in XHR.send method I can't send more than one file. Only one file. 2nd: As written in the PHP Manual "A stream opened with php://input can only be read once". It means again that no more than one request with one file can be made with your technique. Do you have any more solutions how to send more than one file using 'php://input' and File object directly in XHR.send method? I tried to loop xhr.send(file[n]) but the problem with the readystate property apears.
  • Opty
    Opty over 11 years
    Yes indeed, each call to the PHP script is for one file, so if you want to send many related files at the same time, simply allocate a XHR for each files on the client side, then when all the sends are completed, just call one more final php script referring to all files and do what you have to do with those related files. We have "dropbox like" internal webapp that works very well with that method
  • razz
    razz about 11 years
    on the server side how can i use the $filename $filesize and $in to copy the file??! do i use copy() or move_uploaded_file? i have a restriction on my server that wont allow me to upload files bigger than 2 mb! please help
  • Opty
    Opty almost 11 years
    Use while($data = fread($in, 1024)) fwrite($out, $data); with $out a write opened handle to your destination file on the server