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.
Related
I'm trying to make a PSOT request to the Microsoft Oauth2.0 token URL using NodeJS and the XMLHttpRequest library found here. However, I am having the problem, that I just can't send a proper Request body alongside with the Request. I have already tried using FormData from here, I tried URLSearchParams, and I tried making it a String in the way we know it from our Adress bar in GET Requests. Below you can see my Code from when I tried doing it in a GET URL form, and in the part that I made a commend, you can see my attempts of using FormData instead.
var xhr = new XMLHttpRequest();
xhr.open("POST", 'https://login.microsoftonline.com/common/oauth2/v2.0/token');
/*var data = new FormData();
//var data = new URLSearchParams();
data.append('client_id', clientId);
data.append("grant_type", "authorization_code");
data.append("scope", "openid email profile");
data.append("code", code);
data.append("redirect_uri", "http://" + req.headers.host + req.url);
data.append("client_secret", secret);
Error message on this one: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of FormData
Same message with URLSearchParams, except it received an instance of URLSearchParams
*/
var data = 'client_id=' + clientId;
data += '&grant_type=authorization_code';
data += '&scope=openid%20email%20profile';
data += '&code=' + code;
data += '&redirect_uri=' + encodeURIComponent("http://" + req.headers.host + req.url);
data += '&client_secret=' + secret;
//This one gives me an error message from Microsoft: {"error":"invalid_request","error_description":"AADSTS900144: The request body must contain the following parameter: 'grant_type'.\r\nTrace ID: ratherNotSay\r\nCorrelation ID: ratherNotSay\r\nTimestamp: 2020-02-10 10:37:36Z","error_codes":[900144],"timestamp":"2020-02-10 10:37:36Z","trace_id":"ratherNotSay","correlation_id":"ratherNotSay","error_uri":"https://login.microsoftonline.com/error?code=900144"}
//This must mean that the request body can not have been submitted in the right way.
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
console.log(xhr.status + "\n" + xhr.responseText + "\n");
}
};
xhr.send(data);
You can transform a URLSearchParams instance into a query string, like the one that you build manually, by using the toString() method.
I don't know if the Content-Type header is set to application/x-www-form-urlencoded by default in the node-XMLHTTPRequest version, but it couldn't hurt to set it manually.
const xhr = new XMLHttpRequest();
xhr.open("POST", 'https://login.microsoftonline.com/common/oauth2/v2.0/token');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
const data = new URLSearchParams();
data.append('client_id', clientId);
data.append("grant_type", "authorization_code");
data.append("scope", "openid email profile");
data.append("code", code);
data.append("redirect_uri", "http://" + req.headers.host + req.url);
data.append("client_secret", secret);
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
console.log(xhr.status + "\n" + xhr.responseText + "\n");
}
};
xhr.send(data.toString());
We have a scenario where we are recieving document as content-type application/pdf or text/html or text/xml data from first backend, which we need to pass to another backend which will accept the data only as multipart/form-data. I have attached my code snippet. Also how to set boundary value in datapower ?
The data is not reaching as form-data to second backend.
My Gateway code Snippet looks like this :
var urlopen = require('urlopen');
var sm = require('service-metadata');
var hm = require('service-metadata');
var ctx = session.name('message') || session.createContext('message');
session.input.readAsBuffer(function(error, buffer){
if (error) {
//an error occurred when parsing the content,
throw error;
}
var uuid = (session.name('message').getVar('uuid'));
var boundary = (session.name('message').getVar('boundary'));
var ctype = hm.current.get('Content-Type');
var options = {
target:'targeturl',
headers : hm.current.get(),
method: 'post',
data: boundary+ '\n' +" Content-Disposition:form-data; name="+'"'+"fileContent"+'"' + "hello.html" +'"\n' + "Content-Type: text/html" + '\n' + buffer.toString() +'\n' + boundary +'\n'
};
urlopen.open(options,function (error, response) {
if (error) {
session.output.write("openCallback error: " + error.errorMessage+"\n");
}
else {
if (response.statusCode != 200) {
)
It is possible but you will need to build the complete MIME form and set the correct headers for it.
E.g. the Content-Type has to be set something like:
'Content-Type: multipart/form-data; charset=utf-8; boundary=' + boundary;
You will also need to add the MIME boundaries, like:
--my_fine_boundary_1234
Content-Disposition:form-data; name=HELLO
--my_fine_boundary_1234--
Your code doesn't reveal if boundary has the dashes -- or not included but you need to add the starting dashes and the ending dashes and in the content-type header the dashes must NOT be included!
You should also use \r\n for new-lines in the MIME data.
See more about MIME standard here: https://www.ietf.org/rfc/rfc2046.txt
I have a site where the user can design smartphone cases. At one point the user should be able to share the design on Facebook, including the design. I have the object as well as a , with the 'style' set as the data URI of the canvas.
The code for custom image while sharing is:
How would I go to share it with my image, as it's a data URI.
Thanks
UPDATE:
I now have the canvas saved down on the server, linked correctly. Although, I can't seem to be able to edit the "href" of the link Facebook read the thumbnail picture from.
I tried:
var fblink = document.getElementById("facebook_share");
fblink.href="http://example.com/image.png";
AND
fblink.setAttribute("href", "http://example.com/image.png");
None seem to work. The 'fblink' object is correct as I can read the 'rel' etc.
I personally used canvas.toDataURL() which generates a base64 encoded URL of your canvas content.
After that I decoded the URL using the following command Base64Binary.decode(encodedPng)
Once you have your decoded image you can put it in a form and send all that through an XMLHttpRequest object as specified in the code below:
// Random boundary defined to separate element in form
var boundary = '----ThisIsTheBoundary1234567890';
// this is the multipart/form-data boundary we'll use
var formData = '--' + boundary + '\r\n';
formData += 'Content-Disposition: form-data; name="source"; filename="' + filename + '"\r\n';
formData += 'Content-Type: ' + mimeType + '\r\n\r\n';
// let's encode our image file
for ( var i = 0; i < imageData.length; ++i ) {
formData += String.fromCharCode( imageData[ i ] & 0xff );
}
formData += '\r\n';
formData += '--' + boundary + '\r\n';
formData += 'Content-Disposition: form-data; name="message"\r\nContent-Type: text/html; charset=utf-8\r\n\r\n';
formData += message + '\r\n'
formData += '--' + boundary + '--\r\n';
// Create a POST XML Http Request
var xhr = new XMLHttpRequest();
xhr.open( 'POST', 'https://graph.facebook.com/me/photos?access_token=' + authToken, true );
// Call back function if POST request succeed or fail
xhr.onload = xhr.onerror = function() {
if ( !(xhr.responseText.split('"')[1] == "error") ) {
// If there is no error we redirect the user to the FB post she/he just created
var userID = xhr.responseText.split('"')[7].split('_')[0];
var postID = xhr.responseText.split('"')[7].split('_')[1];
w = window.open('https://www.facebook.com/'+userID+'/posts/'+postID,
'width=1235,height=530');
}
else {
alert("Erreur: "+xhr.responseText);
}
};
xhr.setRequestHeader( "Content-Type", "multipart/form-data; boundary=" + boundary );
// Attach the data to the request as binary
xhr.sendAsBinary( formData );
You can see a full working example on my Github project in the file maskToFb.html
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/
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.