post xml as a binary file using javascript - javascript

I'm trying to write something that can post xml, as a binary file to an external URL (which I have no control over) in JavaScript.
I have YUI3 available to me. And probably jQuery if I needed it.
Any ideas? Everything I've looked at seems to be about receiving xml, rather than posting it.
Edit: The external url is an advertising bot, the xml essentially describes what sort of ad I want to get back.
I'm forced to post as binary. I've tested using-
<form enctype="multipart/form-data" action="http://something.com" method="post">
<input name="anything" type="file">something</file>
<input type="submit">
</form>
and that works. I just need to implement in js.
Edit #2
My solution (couldn't get it formatted properly)-
var AdManager = {
getRandomBoundary : function(){
var today = new Date;
return '---' + today.getTime();
},
fetch : function(){
var boundary = this.getRandomBoundary();
var xhr = new XMLHttpRequest;
var CRLF = "\r\n";
xhr.open('POST', 'http://url.com', true);
xhr.onreadystatechange = function(){
if (xhr.readyState === 4)
{
//Parse xml(badly)
var splitter = xhr.responseText.split('<responsecontent>');
var allAds = '';
for (var i= 1; i< splitter.length; i++)
{
var tempAd = splitter[i].split('</responsecontent>');
allAds += tempAd[0];
}
//Html is returned encoded, so decode.
jQuery('#results').html(jQuery("<div/>").html(allAds).text());
}
};
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
var mimeReq = "--" + boundary + CRLF;
mimeReq += 'Content-Disposition: form-data; name="arbitrary"; filename="arbitrary.xml"' + CRLF;
mimeReq += 'Content-Type: application/octet-stream' + CRLF + CRLF;
mimeReq += '<?xml version=\"1.0\" encoding=\"utf-8\"?><adrequestpacket responsemarkup=\"wml\" test=\"0\" xmlns=...'+ CRLF;
mimeReq += "--" + boundary + "--" + CRLF;
xhr.send(mimeReq);
}
};
`

I think I understand what your asking, but if I'm totally on the wrong track, the below may appear a little patronising, so apologies in advance...
If all you want to do is send an XML file to a known URL via AJAX its fairly simple in javascript with no lovelies like jQuery etc. I am assuming you have already generated the XML file and have it stored as string variable somewhere.
The below code is a bit messy and fairly basic, but hopefully it should point you in the right direction. There are probably better ways of fetching an AJAX object if you search for them, but this is a method I have used for ages and never really have any problems with.
You will need to write some code to parse the server response to determine whether data was accepted or not - see comments in code for where you would do this. The ajaxObj.status and ajaxObj.responseText properties will be your friends here.
function postXMLToServer (serverURL, xmlStr) {
// Declare some variables
var activeXModes, i, ajaxObj, aSync, contentType;
// Set this to false to perform the request synchronously (i.e. execution will block until request has completed)
aSync = true;
// 'application/octet-stream' is treated as raw binary data by any sensible server.
// It might make more sense to use 'text/xml' or some variant depending on your application
contentType = 'application/octet-stream';
// Fetch an AJAX object
activeXModes = ["Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
if (window.ActiveXObject) { // Try ActiveX (for IE)
for (i = 0; i < activeXModes.length; i++) {
try {
ajaxObj = new ActiveXObject(activeXModes[i]);
} catch (e) {}
}
} else if (window.XMLHttpRequest) { // For Mozilla, Safari etc
ajaxObj = new XMLHttpRequest();
} else { // No AJAX
alert('AJAX is not supported in your browser');
return;
}
// Open connection to server
ajaxObj.open('POST',serverURL,aSync);
// Set some request headers - you might be able to get away with not doing this, but it
// should be considered good practice, especially when doing POST requests
ajaxObj.setRequestHeader('Content-Type',contentType);
ajaxObj.setRequestHeader('Content-Length',xmlStr.length);
// Set a callback for asynchronous requests (not called if aSync = false)
ajaxObj.onreadystatechange = function () {
if (ajaxObj.readyState == 4) {
// parse the server response here
}
};
// Send the request
ajaxObj.send(xmlStr);
// if aSync = false, parse the server response here
}
// Example of how to use the function
var myXMLStr = '<?xml version="1.0" encoding="iso-8859-1" ?>\n<toplevel>\n<lowerlevel anattribute="a value">An inner value</lowerlevel>\n</toplevel>';
var myURL = 'http://sub.domain.tld/path/to/document.ext?getparameter=somevalue';
postXMLToServer(myURL,myXMLStr);

It's not entirely clear what you want. Everything in the computer is represented in binary. So when you post an XML document over to http://something.com, the it's the binary representation of the characters in the XML file that is being transmitted.

Related

XMLHttpRequest - Which format does request.send(data) expect for data?

I have to use a XMLHttpRequest, but I don't know which data format is expected by the function request.send(). I searched for too long now.
I tried to pass a JSON object but it does not work:
var request = new XMLHttpRequest();
request.open("GET","fileApi");
var data = {
action: "read",
targetFile: "testFile"
};
request.addEventListener('load', function() {
if (request.status >= 200 && request.status < 300) {
$("#messageOutput").html(request.responseText);
} else {
console.warn(request.statusText, request.responseText);
}
});
request.send(data);
I get updateFile:155 XHR finished loading: GET "http://localhost/cut/public/fileApi".
But no data is received on the server. I made this simple check to approve this:
PHP (server side):
$action = filter_input(INPUT_GET, "action");
$targetFile = filter_input(INPUT_GET, "targetFile");
echo ("action = '$action' | targetFile = '$targetFile'");
exit();
Returns: action = '' | targetFile = ''
Unfortunatelly I can't use jQuery in my application, since the target is a C# Webbrowser (Internet Explorer) and it detects errors in the jQuery file and stops my scripts from working...
I don't know which data format is expected by the function request.send()
It can take a variety of formats. A string or a FormData object is most common. It will, in part, depend on what the server is expecting.
I tried to pass a JSON object
That's a JavaScript object, not a JSON object.
request.open("GET","fileApi");
You are making a GET request. GET requests should not have a request body, so you shouldn't pass any data to send() at all.
GET requests expect data to be encoded in the query string of the URL.
var data = {
action: "read",
targetFile: "testFile"
};
var searchParams = new URLSearchParams();
Object.keys(data).forEach((key) => searchParams.set(key, data[key]));
var url = "fileApi?" + searchParams;
console.log(url);
// and then…
// request.open("GET", url);
// request.send();
Warning: URLSearchParams is new and has limited browser support. Finding a library to generate a query string is left as a (simple) exercise to any reader who wants compatibility with older browsers.

Javascript binary string concatenation on form-data in XMLHttpRequest

I'm writing a Chrome extension, and need to build a custom Form data to upload a Zip file (I can't use a real HTML form) to a server (not mine).
I found a way to do it here -
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms/Sending_forms_through_JavaScript (last section - Dealing with binary data).
It works fine with clear text files, but I want to send Zip which is binary.
It seems that JavaScript can't handle concatenation of binary and non binary, and adds some 0Xc2 chars to my file )-:
I also found a solution at http://footle.org/2007/07/31/binary-multipart-posts-in-javascript/,
but it uses Components.classes["#mozilla.org/io/string-input-stream;1"], which is not usable in Chrome extension.
How can I concatenate a binary zip file with a string and upload it to a server?
function sendData(zipBinary) {
var XHR = new XMLHttpRequest();
var boundary = "----WebKitFormBoundary3n9vu9ZOkCPW4HAw";
var prefix = "";
var postfix = "";
prefix += "--" + boundary + "\r\n";
prefix += 'content-disposition: form-data; '
+ 'name="' + 'UploadedFile' + '"; '
+ 'filename="' + 'hello.zip' + '"\r\n';
prefix += 'Content-Type: ' + 'application/x-zip-compressed' + '\r\n';
prefix += '\r\n';
postfix += '\r\n';
// Once we are done, we "close" the body's request
postfix += "--" + boundary + "--";
postfix += '\r\n';
XHR.addEventListener('load', function(event) {
alert('Yeah! Data sent and response loaded.');
});
XHR.addEventListener('error', function(event) {
alert('Oups! Something goes wrong.');
});
XHR.open('POST', 'https://example.com');
XHR.setRequestHeader('Content-Type','multipart/form-data; boundary=' + boundary);
XHR.send(prefix + zipBinary + postfix); // <----
}
Have you read the section about FormData in the article that you've linked? Because that is the best solution for sending files via the XMLHttpRequest API. The advantage of this API over string concatenation is that files can be uploaded in a streamed fashion, instead of having to be entirely being buffered in memory before being sent off.
Assuming that zipBinary is a Blob or File object, you can upload a file as follows:
function sendData(zipBinary) {
var xhr = new XMLHttpRequest();
var fd = new Formdata();
fd.append('hello.zip', zipBinary);
xhr.onload = function() {
// Request completed! Use xhr.status and/or xhr.responseText to
// check the server's response status code and response body.
};
xhr.onerror = function() {
// Aw. Network error.
};
xhr.open('POST', 'https://example.com/');
xhr.send(fd);
}
If zipBinary is not a Blob or File object, but a string of binary data, then you can convert it to a Blob with a specified MIME-type as follows:
function sendData(zipBinary) {
var zipBinaryBytes = new Uint8Array(zipBinary.length);
for (var i = 0; i < zipBinary.length; ++i) {
zipBinaryBytes[i] = zipBinary.charCodeAt(i);
}
zipBinary = new Blob([zipBinaryBytes], { type: 'application/zip' });
// rest of code as I suggested above...
(note: application/zip is the official MIME-type for zip files, not application/x-zip-compressed)
If you are an expert of the HTTP protocol and really want to write the HTTP request body fully by hand, then you need to send a typed array instead of a string, because the XMLHttpRequest API will encode a regular string as UTF-8 before passing off the data to the server. This can be done using the TextEncoder API since Chrome 38+, applied to your code as follows:
// Was: XHR.send(prefix + zipBinary + postfix);
// New
XHR.send(new TextEncoder().encode(prefix + zipBinary + postfix));
Note: You rarely need to construct the request body by hand. Unless you know what you're doing, having to manually construct the request body is usually an indicator that you are doing it wrong.

Upload an image file using the request object

In the Firefox extension I am currently developing, I've been trying for days now to upload an image file to a server using the request module of the Firefox Add-on SDK.
I managed to upload a text file but I cannot upload any other kind of files.
My final goal is to upload a screenshot so I really need to be able to upload an image.
Here is my current code:
params= file.read("C:\\FullPath\h3nf5v2c.png", "b");
//multipart form data
boundary = "---------------------------132611019532525";
var snapShotUpload = Request({
url : "https://myurl.com",
headers : {
"Referer" : "https://myurl.com",
"Content-Type" : "multipart/form-data; boundary=" + boundary,
},
content : "--" + boundary + "\r\n" + "Content-Disposition: form-data; name='Upload_FileName'; filename='h3nf5v2c.png'\r\nContent-Type: application/octet-stream\r\n\r\n" + params + "\r\n--" + boundary + "--\r\n",
onComplete: function(response) {
console.log(response.text);
}
});
console.log("request built");
snapShotUpload.post();
The uploaded image is corrupted and I can't read it.
So my question is:
How do I post an image using the request module of the Firefox Add-on SDK?
Thank you Wladimir,
I didn't actually modify the Request module but simply used an XMLHttpRequest instead.
Here is the code I used if anybody is interested:
function sendImage(file){
fName = "h3nf5v2c.png";
// prepare the MIME POST data
var boundaryString = '---------------------------132611019532525';
var boundary = '--' + boundaryString;
var requestbody = boundary + '\r\n'
+ 'Content-Disposition: form-data; name="Upload_FileName"; filename="'
+ fName + '"' + '\r\n'
+ 'Content-Type: image/png' + '\r\n'
+ '\r\n'
+ file.read()
+ '\r\n'
+ boundary + '--\r\n';
// Send
var http_request = Components.classes["#mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Components.interfaces.nsIXMLHttpRequest);
http_request.onreadystatechange = function() {
if (http_request.readyState == 4 && http_request.status == 200) {
console.log(http_request.responseText);
}
};
http_request.open('POST', 'http://myUrl.com', true);
http_request.setRequestHeader("Referer", "http://myUrl.com");
http_request.setRequestHeader("Content-type", "multipart/form-data; boundary=" + boundaryString);
//http_request.setRequestHeader("Connection", "close");
http_request.setRequestHeader("Content-length", requestbody.length);
http_request.sendAsBinary(requestbody);
}
The file parameter of the function is a file object from the file module.
The sendAsBinary function at the end is very important because I'm working client side only and I had to simulate an insert via a form.
If you have access to the server side, sending a base64 encoded version of the image and decoding it server-side is probably simpler.
Unfortunately, the answer is: you cannot. The request module is mainly meant to transmit url-encoded data so anything that isn't a string is assumed to be an object containing key-value pairs that need to be encoded. So sending a string is your only option if you need to send multipart content yet XMLHttpRequest will always encode strings as UTF-8 which will produce rather undesirable results when you are trying to send raw binary data (such as an image data).
It would be much easier to use a FormData object but even if you create one - the request module won't pass it to XMLHttpRequest. There is no simple solution short of modifying the request module (file packages/addon-kit/lib/request.js of the Add-on SDK). Find this line:
let data = stringify(content);
Changing it into the following code should work:
let {Ci} = require("chrome");
let data = content;
if (!(content instanceof Ci.nsIDOMFormData))
data = stringify(content);
This makes sure that FormData objects aren't changed.
Note that you might not have FormData and File defined in your main.js. If that's the case "stealing" them from any JavaScript module should work:
let {Cu} = require("chrome");
var {FormData, File} = Cu.import("resource://gre/modules/Services.jsm")
I think there is another way to post an image on your server. You can use the base64 encoding.
I never test it but I found this subject on the web which explains how to convert the image. I hope this can help you.
What can simplify your life is using FormData and XMLHttpRequests together as in this example.
function uploadFiles(url, files) {
var formData = new FormData();
for (var i = 0, file; file = files[i]; ++i) {
formData.append(file.name, file);
}
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.onload = function(e) { ... };
xhr.send(formData); // multipart/form-data
}
document.querySelector('input[type="file"]').addEventListener('change', function(e) {
uploadFiles('/server', this.files);
}, false);
source: http://www.html5rocks.com/en/tutorials/file/xhr2/

Using XHR to send large amounts of data via POST

I have an XHR that uploads large amounts of data to the server. This XHR dies intermittently and seemingly without pattern. Currently my code looks like this:
function makeFormData(data) {
var formdata = "";
for (var key in data) {
formdata = formdata + "&" + key + "=" + encodeURIComponent(data[key]);
}
return formdata.slice(1);
}
function xhr(url, data, onsuccess, onerror) {
var xhrequest = new XMLHttpRequest();
xhrequest.onreadystatechange = function () {
if (xhrequest.readyState != 4)
return;
if (xhrequest.responseText)
onsuccess(xhrequest.responseText);
}
xhrequest.onerror = function (error_param) {
onerror(error_param);
}
xhrequest.open('POST', url, true);
xhrequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhrequest.send(makeFormData(data));
}
This is in a Chrome extension. What am I doing wrong? How can I make the form multipart instead, to make the upload smaller? I have used Wireshark to trace the request and the it cuts off mid-send with many packet retransmissions (and never completes).
Please help me, this is driving me crazy.
check the server directives. Maybe the max_execution_time and upload size have tiny values. If the js don't perform request try debug it.

How to get JSON object in Servlet from JSP?

In JSP page I have written:
var sel = document.getElementById("Wimax");
var ip = sel.options[sel.selectedIndex].value;
var param;
var url = 'ConfigurationServlet?ActionID=Configuration_Physical_Get';
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
httpRequest.open("POST", url, true);
httpRequest.onreadystatechange = handler(){
if (httpRequest.readyState == 4) {
if (httpRequest.status == 200) {
param = 'ip='+ip;
param += 'mmv='+mmv;
param += "tab="+tab;
}};
httpRequest.send(param);
I want this param variable in my ConfigurationServlet. Can anyone tell me how to get this json object in servlet?
Update: I changed my statements and now it is showing status code as 200.
var index = document.getElementById("Wimax").selectedIndex;
var ip = document.getElementById("Wimax").options[index].text;
httpReq = GetXmlHttpObject();
alert(httpReq);
var param = "ip=" + ip;
param += "&mmv=" + mmv;
param += "&tab=" + tab;
alert("param "+param);
var url="http://localhost:8080/WiMaxNM/ConfigurationServlet?ActionID=Configuration_Physical_Get";
url = url+"?"+param;
httpReq.open("GET",url,true);
alert("httpReq "+httpReq);
httpReq.onreadystatechange = handler;
httpReq.send(null);
But new problem has occured. Control is not at all going to the servlet action ID as specified in url. Please tell me what is wrong here.
The code in the handler will only be invoked AFTER the request is been sent. You need to populate param before this. You would also need to concatentate separate parameters by &.
Thus, e.g.
// ...
httpRequest.onreadystatechange = handler() {
// Write code here which should be executed when the request state has changed.
if (httpRequest.readyState == 4) {
// Write code here which should be executed when the request is completed.
if (httpRequest.status == 200) {
// Write code here which should be executed when the request is succesful.
}
}
};
param = 'ip=' + ip;
param += '&mmv=' + mmv;
param += "&tab=" + tab;
httpRequest.send(param);
Then you can access them in the servlet the usual HttpServletRequest#getParameter() way.
That said, the Ajax code you posted there will only work in Microsoft Internet Explorer, not in all the four other major webbrowsers the world is aware of. In other words, your Javascript code won't work for about half of the people in the world.
I suggest to have a look at jQuery to lessen all the verbose work and bridge the crossbrowser compatibility pains. All your code could be easily replaced by
var params = {
ip: $("Wimax").val();
mmv: mmv,
tab: tab
};
$.post('ConfigurationServlet?ActionID=Configuration_Physical_Get', params);
And still work in all webbrowsers!
Update: as per your update, the final URL is plain wrong. The ? denotes a start of the query string. You already have one in your URL. You should use & to chain parameters in the query string. I.e.
url = url + "&" + param;

Categories