Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send multipart/form-data form content by ajax (no jquery)?

I am trying to send some form without reloading the page and I am trying to understand the under-the-hood details therefore not using any JavaScript library:

var http = createRequestObject();
function createRequestObject() {
    var objAjax;
    if (window.XMLHttpRequest)
    {// code for IE7+, Firefox, Chrome, Opera, Safari
        objAjax=new XMLHttpRequest();
    }
    else
    {// code for IE6, IE5
        objAjax=new ActiveXObject("Microsoft.XMLHTTP");
    }
    return objAjax;
}

function display_progress() { ... }    

function upload_file() {
    var request = 'UploaderServlet';
    http.open('post', request);
    http.onreadystatechange = display_progress;
    http.send(null); // HERE PROBABLY THE DATA SHOULD BE SENT
}

<form enctype="multipart/form-data" id="upload_form" name="upload_form" method="POST" action="UploaderServlet" onsubmit="upload_file(); return false;" target="upload_target">
Choose a file <br />  
<input name="file" size="27" type="file" id="file" /> <br/> 
<input type="submit" name="uploadSubmitButton" value="Upload" /><br /> 
<br />  
</form> 

<iframe id="upload_target" name="upload_target" src="#" style="width:0;height:0;border:0px solid #fff;"></iframe>

The upload_file() is called, but if I get it right, the data is not sent. Please advice regarding the correct way to send the data.

like image 918
BreakPhreak Avatar asked May 09 '11 07:05

BreakPhreak


2 Answers

Pass attribute in form {url:"",method:"",data:{...},callback:function(){}}

var ajax=function(){
  try{
    var xml       =new XMLHttpRequest();
    var args      =arguments;
    var context   =this;
    var multipart ="";

    xml.open(args[0].method,args[0].url,true);

    if(args[0].method.search(/post/i)!=-1){
      var boundary=Math.random().toString().substr(2);
      xml.setRequestHeader("content-type",
                  "multipart/form-data; charset=utf-8; boundary=" + boundary);
      for(var key in args[0].data){
        multipart += "--" + boundary
                   + "\r\nContent-Disposition: form-data; name=" + key
                   + "\r\nContent-type: application/octet-stream"
                   + "\r\n\r\n" + args[0].data[key] + "\r\n";
      }
      multipart += "--"+boundary+"--\r\n";
    }

    xml.onreadystatechange=function(){
      try{
        if(xml.readyState==4){
          context.txt=xml.responseText;
          context.xml=xml.responseXML;
          args[0].callback();
        }
      }
      catch(e){}
    }

    xml.send(multipart);
  }
  catch(e){}
}

If you want to get back response you can use this

var response={};
ajax.call(response,{...args...})

and you can retrieve all data by response.txt or response.xml

A bit late update

As for @Varun question about <input type='file'> uploads, this code can't handle file uploads directly, in order to send files using this code, you need to perform preprocessing of the raw file data using File API to get non-binary streams (like base64 or any other bin2hex-like form).

But, since it's a 2015 year, I can suggest to move from the construction of the multipart streams to something a bit more robust, like the FormData API.

like image 82
excanoe Avatar answered Oct 18 '22 01:10

excanoe


Your XMLHttpRequest code looks fine other than the null you're passing into send. You need to pass in a string containing the data to send (properly encoded, of course).

Note that if you want to send the file referenced by your input[type=file] field, you'll need to read it into memory, and the only way to do that is to use the new File API, which isn't broadly-supported yet.

If you're trying to send a file with a progress bar, you could do that by posting blocks of it you've read via the File API on a browser that supports the File API, perhaps fall back to a Flash-based uploader like swfupload if the browser doesn't support the File API, and fall back to a normal form submission if the browser doesn't support Flash.

like image 1
T.J. Crowder Avatar answered Oct 18 '22 02:10

T.J. Crowder