First Prev 28 of 42 Next Last

Using XMLHttpRequest to Download Binary Data

Posted in .../weblog on 2009-03-01 20:30:00

I was working on a userscript last week to facilitate downloading and saving images from a facebook album. I wanted to automate this, rather than have to manually open and save each picture. The Greasemonkey add-on to Firefox seemed like the best way to get this done, and before long I had a bit of JavaScript that could fetch and save a file to my local disk with no user interaction. In the spirit of logging things I might want to come back to later, join saving other people the frustration I experienced with all my searches finding metablogs referring to the same ghostblog, I briefly document what I needed.

First, it seems sensible to cover downloading binary data via Javascript. I used the XMLHttpRequest object for this, which I created in this cross-browser way:

var oXML=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();

My first attempts at downloading images would produce data, but not a viable image. It turned out to be affected by the way XMLHttpRequest process the HTTP response, which seemed hopeless at first, but proved to be easy to resolve, given the right information. Fetching binary data is a two-step process. The request needs to be configured properly, and the response requires some processing to restore the data to it's original form. The configuration of the request is simple, one additional line of code:

oXML.overrideMimeType('text/plain; charset=x-user-defined');

Post-processing is as simple as striping the high-order bytes from each character in the response text and building a new (binary) string from the low-order bytes:

function repairBinary(sIn){ var sOut=[]; for(var i=0;i<sIn.length;i++){ sOut[i]=String.fromCharCode(sIn.charCodeAt(i) & 0xff); } return sOut.join(''); }

Finally, I need a method for automated file saving:

function writeFile(sFilename,sData) { if(!sFilename)return; netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); file.initWithPath( sFilename ); stream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); stream.init(file, -1, -1, null); stream.write(sData, sData.length); stream.flush(); stream.close(); }

The above requires a configuration change in Firefox. Open about:config and check that [signed.applets.codebase_principal_support] is set to "true". This will allow the browser to prompt the user for access to write the local file. Clicking "Remember this decision" and "Allow" will save the user from having to answer the question for each file, so long as they stay on the same page. It is not globally remembered, so there is no worry about other sites abusing this permission. Of course, since this is a user script, one could just unset the option when done.

1 Comments

  1. Comment Removed by moderator

Submit a Comment

moderation enabled