2

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?

Wladimir Palant
  • 56,865
  • 12
  • 98
  • 126
manu
  • 1,059
  • 1
  • 16
  • 33

4 Answers4

2

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.

manu
  • 1,059
  • 1
  • 16
  • 33
1

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")
Wladimir Palant
  • 56,865
  • 12
  • 98
  • 126
0

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.

Community
  • 1
  • 1
Charles Jourdan
  • 809
  • 7
  • 17
  • Yes, I had already thought of that, but I didn't work, my server will only accept binary files. But I'm going to try Mr Palant 's technique. Thank you – manu Sep 26 '12 at 16:46
0

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/

EdoPut
  • 1