3

Tried to use the following code, but it doesn't work properly:

  // download the file first
  var req = new XMLHttpRequest();
  req.open('GET', url, false);
  req.overrideMimeType('text/plain; charset=x-user-defined');
  req.send(null);
  if (req.status != 200) return '';

  // upload the file
  req.open("POST", "http://mysite.com/upload", false);
  req.setRequestHeader("Content-Length", req.responseText.length);
  req.sendAsBinary(req.responseText); // What should I pass here?

  if (req.status != 200) return '';
  return req.responseText;

sendAsBinary is firefox function.

Upd. Also I've tried to upload that as part of the form:

var response = req.responseText;
var formData = new FormData();
formData.append("file", response);
req.open("POST", "http://mysite.com/upload", false);
req.send(formData);

But still not full data is received by the server.

LA_
  • 19,823
  • 58
  • 172
  • 308

2 Answers2

3

Finally I've used the approach with temp file:

  var downloadCompleted = false;

  // download the file first
  var persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
                  .createInstance(Components.interfaces.nsIWebBrowserPersist);
  // get OS temp folder
  var file = Components.classes["@mozilla.org/file/directory_service;1"]
                .getService(Components.interfaces.nsIProperties)
                .get("TmpD", Components.interfaces.nsIFile);
  file.append("temp.ext");
  file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);

  var fURI = Services.io.newURI(url,null,null);
    const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
    const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
    persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;

  persist.progressListener = {
    onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
      },
    onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
      if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) {
         downloadCompleted = true; // file has been downloaded        
      }
    }
  }
  persist.saveURI(fURI, null, null, null, "", file);  

  var thread = Components.classes["@mozilla.org/thread-manager;1"]
                        .getService(Components.interfaces.nsIThreadManager)
                        .currentThread;
  while (!downloadCompleted) // emulate synchronous request, not recommended approach
    thread.processNextEvent(true); 

  // upload the file
  var stream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                       .createInstance(Components.interfaces.nsIFileInputStream);
  stream.init(file, 0x04 | 0x08, 0644, 0x04); // file is an nsIFile instance   

  // try to determine the MIME type of the file
  var mimeType = "text/plain";
  try {
    var mimeService = Components.classes["@mozilla.org/mime;1"]
            .getService(Components.interfaces.nsIMIMEService);
    mimeType = mimeService.getTypeFromFile(file); // file is an nsIFile instance
  }
  catch(e) { /* just use text/plain */ }

  var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                      .createInstance(Components.interfaces.nsIXMLHttpRequest);
  req.open('POST', "http://mysite.com/upload", false); 
  req.setRequestHeader('Content-Type', mimeType);
  req.send(stream);

  // delete the file
  file.remove(false);
LA_
  • 19,823
  • 58
  • 172
  • 308
2

You need to store the responseText in an intermediate variable before reusing the req object.

// download the file first
var req = new XMLHttpRequest();
req.open('GET', url, false);
req.overrideMimeType('text/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) return '';
var response = req.responseText;

// upload the file
req.open("POST", "http://mysite.com/upload", false);
req.setRequestHeader("Content-Length", response.length);
req.sendAsBinary(response);
if (req.status != 200) return '';
return req.responseText;

Update

Per the MDN page Using XMLHttpRequest, it looks like the above code won't work. Following is the proper way to get the binary response. In the end, you will have an array of unsigned integers which you could send back to the server and convert to binary. I think.

//req.responseType is only defined for FF6+
req.responseType = "arraybuffer";
req.send(null);

//req.response is for FF6+, req.mozResponseArrayBuffer is for FF < 6
var buffer = req.mozResponseArrayBuffer || req.response;
if (buffer) {
  var byteArray = new Uint8Array(buffer);
}

Update 2

To submit the byteArray to a server, I would try something like the following untested, almost guaranteed not to work code.

req.open("POST", "http://mysite.com/upload", false);
req.setRequestHeader("Content-Length", byteArray.length);
//if this doesn't work, try byteArray.buffer
//if byteArray.buffer works, try skipping 'var byteArray = new Uint8Array(buffer);' altogether and just sending the buffer directly
req.send(byteArray); 

Update 3

Could Using XMLHttpRequest from JavaScript modules / XPCOM components have anything to do with your issue?

Chris Shouts
  • 5,377
  • 2
  • 29
  • 40
  • Hmm. Still server part shows that it received file with 0 size. Probably, my server code is wrong. Is there any way to pass the file as part of the form? (then I know for sure how to get that on the server side) – LA_ Jul 28 '11 at 19:19
  • You could probably store a base64 encoded string representation of the binary file in a hidden input on the form. When you post the form, the base64 string should be posted as well. – Chris Shouts Jul 28 '11 at 19:27
  • Chris, how should I do that? I've update the question with my attempt. – LA_ Jul 28 '11 at 19:28
  • OK, since this is firefox addon, I can use `window.btoa(unescape(encodeURIComponent(req.responseText)))`. That works correctly. But the file size is increased by 3 times! And I have to decode that on the server side. Is there any other way? – LA_ Jul 28 '11 at 19:37
  • Chris, but how should I upload that `byteArray` then? – LA_ Jul 28 '11 at 21:10
  • Chris, thanks for your help. I tried again usage of this FF-specific properties - download works well (at least `byteArray.length` returns correct file length). However, if I try to upload data with `req.send` (I've tried that with `byteArray`, `byteArray.buffer` and `buffer`) - nothing is sent to the server. If I do the same with `req.sendAsBinary` - then 19 or 20 bytes are sent. – LA_ Jul 29 '11 at 19:08
  • @LA_ Have you tried creating a `new XMLHttpRequest()` object for uploading the file? I have never tried to reuse an `XMLHttpRequest` object to make multiple requests, so I am unsure about its behavior. – Chris Shouts Jul 29 '11 at 19:15
  • yes, I have tested everything what I've described above with new XMLHttpRequest. – LA_ Jul 29 '11 at 19:19
  • @LA_ I wonder if this is an [XSS](http://stackoverflow.com/questions/298745/how-do-i-send-a-cross-domain-post-request-via-javascript) issue? Perhaps FF is preventing you from making a request to `http://mysite.com`? – Chris Shouts Jul 29 '11 at 20:12
  • @LA_ let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/1969/discussion-between-chris-shouts-and-la) – Chris Shouts Jul 29 '11 at 20:12