2

If I have an in-memory string in JavaScript that is let's say Excel or PDF format, and I want to pop open a save dialog so the user can save those bytes to a file, how would I go about doing this? I am trying to avoid going back to the server. If I was going back to the server I could send the correct HTTP headers in the response to tell the browser that I'm sending a file. But I want to do this from JavaScript instead because I already have the bytes I need on the client.

Is this possible?

Edit:
I should clarify what I'm actually looking for here. I am working with a Silverlight app. From Silverlight, I can pop up a save dialog and save the bytes (in this case, let's say they are Excel bytes). This seems to be what people are suggesting below when they suggest using Flash. Silverlight gives the same functionality.

But, I would prefer the the Excel file just opens in a new browser window. I could do that pretty easily if I was generating the file on the server, because I could just send the correct headers. But I already have the bytes in Silverlight on the client. Any way to open that doc in a new browser window so the user can just hit an Open button without having to pick a save location and navigate to the file?

And I can't use the out-of-browser application option. I know it would be possible using that by talking with Excel through COM-interop. But that's a no go in this case.

But I can interop with JavaScript from Silverlight. So I was hoping I could use JavaScript in some way to open a browser window and stream the Excel bytes to it.

RationalGeek
  • 9,425
  • 11
  • 62
  • 90

4 Answers4

4

You can use data URIs to embed the file in the HTML document; e.g., http://jsfiddle.net/dqWae/ creates a link that initiates a download of the Wikipedia title image. (The MIME type is hard coded as application/octet-stream but you can of course specify it as PDF or XLS).

You will need to encode the bytes of the file as base 64 and create a data URI for the resource. Then, create a new anchor element whose href attribute is the data URI. If you want to automatically initiate the download, programmatically issue a click event to the anchor element.

Community
  • 1
  • 1
ide
  • 19,942
  • 5
  • 64
  • 106
  • Hmmm, that is pretty interesting. Is this a practical solution in a cross-browser environment. A lot of the users are using IE 6. I'm not sure that it supports these data URIs. – RationalGeek Feb 22 '11 at 19:04
  • IE8+ and the rest of the browser world only. – ide Feb 22 '11 at 19:06
1

In JavaScript? I hope not, think of all the millions of security implications that would cause..

servermanfail
  • 2,532
  • 20
  • 21
  • JavaScript does not have access to the file system on the client machine. This isn't going to be possible. You must use a Server Language (PHP, Ruby, C++, etc) to handle file system access. – g.d.d.c Feb 22 '11 at 18:45
  • 1
    I don't want to access the filesystem directly. I just want to send a string to the browser saying "hey here's a file. pop up a save dialog for the user". The same thing the browser would do if I sent a byte stream from the server with the correct headers. – RationalGeek Feb 22 '11 at 18:50
1

In pure javascript this will be possible only with html5 but not for now.

but you can do some javascript/flash 10 bridge, you pass the bytes to Flash who open the "save as" dialog and then you can save 100% client side.

The flash can be loaded dynamically.

see some code here : http://sujitreddyg.wordpress.com/2008/11/04/filereference-in-flash-player-10/

jujule
  • 11,125
  • 3
  • 42
  • 63
0

There are one non cross-browser solution (works only in Firefox, and only when user grant permissions).

// ask for security grants
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

var pngBinary = 'some string';

var aFile = Components.classes["@mozilla.org/file/local;1"].
            createInstance(Components.interfaces.nsILocalFile);
aFile.initWithPath( "/tmp/somefile.png" );
aFile.createUnique( Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 600);
var stream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"].
             createInstance(Components.interfaces.nsIFileOutputStream);
stream.init(aFile, 0x04 | 0x08 | 0x20, 0600, 0);
stream.write(pngBinary, pngBinary.length);
if (stream instanceof Components.interfaces.nsISafeOutputStream) {
    stream.finish();
} else {
    stream.close();
}

But I recommend you to make cross-browser applications and choose Flash solution.

Gleb M Borisov
  • 617
  • 3
  • 10