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.
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.
In JavaScript? I hope not, think of all the millions of security implications that would cause..
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/
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.
Related
I have a PDF file as a blob object. I want to serve to my users, and right now I'm doing:
html = '<iframe src="' + URL.createURL(blob) + '">';
That works fine for people that want to use their in-browser PDF tool.
But...some people have their browser set to automatically download PDFs. For those people, the name of the downloaded file is some random string based on the blob URL. That's a bad experience for them.
I know I can also do:
<a href="blobURL" download="some-filename.pdf">
But that's a bad experience for the people who want to use in-browser PDF readers, since it forces them to download the file.
Is there a way to make everybody have good file names and to allow everybody to read the PDF the way they want to (in their browser or in their OS's reader)?
Thanks
At least looking at Google Chrome, if the user disables the PDF Viewer (using the option "Download PDF files instead of automatically opening them in Chrome") then window.navigator.plugins will show neither "Chromium PDF Plugin" nor "Chromium PDF Viewer". If the option is left at the default setting, the viewer will show in the plugin list.
Using this method, one can utilize window.navigator.plugins to check if any of the elements' names are either of the aforementioned plugins. Then, depending upon that result, either display a <iframe> or a <a href="blobUrl" download="file.pdf">. For other browsers I imagine that different methods would have to be used. You can also check for a "Acrobat Reader" plugin, which some machines may have instead, or even just the word "PDF".
On a side note, it does look like it is possible to detect if the default Firefox PDF viewer is enabled by using http://www.pinlady.net/PluginDetect/PDFjs/ .
Try to append &filename=thename.pdf to the binary, metadata or http header:
Content-Disposition: attachment; filename="thename.pdf"
I have looked through the documentation of createObjectURL(blob), it will always return a unique and specific format of url. It is not possible to change the URL here.
The plugin thing is not consistent across browsers.
Now here is my radical idea
Find or create(if not available) a js library that can create and save PDF files to server from blob. (I looked through some of them like 'jsPDF','pdfkit' but none of them use blob)
Save the file to server with a valid name
use the above name in the iframe.
This question already has answers here:
Closed 10 years ago.
I have had trouble when researching or otherwise trying to figure out how (if it's even possible) to get binary image data using JavaScript/jQuery from an html input element of type file.
I'm using WebMatrix (C#), but it may not be necessary to know that, if the purposes of this question can be answered using JavaScript/jQuery alone.
I can take the image, save it in the database (as binary data), then later show the pic on the page, from the binary data, after posting. This does, however, leave me without a pic preview, before uploading, for which I am almost certain I must use AJAX.
Again, this may not even be possible, but as long as I can get the binary image data, I believe I can push it to the server with AJAX and process the image the same way I would if I were taking it from a database (note that I don't save the image files themselves using GUID and all that,I just save the binary data).
If there is an easier way to show a pic preview using the input element, that would work fine, too, of course, as the whole idea behind me trying to do this is to show a pic preview before they hit the submit form button (or at least create that illusion).
**********UPDATE***********
I do not consider this a duplicate of another question because, my real question is:
How can I get image data from an input type "file", with JavaScript/jQuery?
If I can just get the data (in the right format) back to the server, I should be able to work with it there, and then return it with AJAX (although, I am absolutely no AJAX expert).
There is, according to the research that I have done, NO WAY to get picture previews in all IE versions using only javascript (this is because getting the full file path is seen, by them, as a potential security risk). I could ask my users to add the site to the trusted sites, but you don't usually ask users to tamper with those kinds of settings (not to mention the quickest way to make your site seem suspicious to users is to ask them to directly add your site to the trusted sites list. That's like sending an email and asking for a password. "Just trust me! I'm soooo safe!" :)
Short answer: Use the jQuery form plugin, it suports AJAX-like form submits even for file uploads.
tl;dr
Thumbnail preview is popular websites is usually done by a number of steps, basically the website do these steps:
upload the RAW image
Resize and optimise the image for data storage
Generate a temporary link to that file (usually stored in a server maintained HTTP session)
Send it back to the user, to enable a 'preview'
Actually store the image after user confirms the image
A few bad solutions are:
Most of the modern browsers has options to enable script access to local files, but usually you don't ask your users to tinker with those low level settings.
Earlier Internet Explorer (ah... yes it's a shame) and ancient versions of modern browsers will expose the full file path by reading the 'value' of file input box, which you can directly generates an tag and use that value. (Now it is replaced by some c:/fakepath/... thing.)
Use Adobe Flash to mimic the file selection panel, it can properly read local files. But passing it into JavaScript is another topic...
Hope these helps. ;)
UPDATE
I actually came across a situation that requires a preview before uploading, I'd like to also put it here. As I could recall, there were no transitional versions in modern browsers that do not implement FileReader before masking the real file path, but feel free to correct me if so. This solution should caters most of the browsers, as long as they are supported by jQuery.
// 1. Listen to change event
$(':file').change(function() {
// 2. Check if it has the FileReader class
if (!this.files) {
// 2.1. Old enough to assume a real path
setPreview(this.value);
}
else {
// 2.2. Read the file content.
var reader = new FileReader();
reader.onload = function() {
setPreview(reader.result);
};
reader.readAsDataURL();
}
});
function setPreview(url) {
// Do preview things.
$('.preview').attr('src', url);
}
I am accessing an existing WCF web service (which returns a PDF as a byte stream) using jquery's ajax methods.
When the call to the service completes, I end up with a javascript variable containing a PDF (the variable has the binary data in, starting "%PDF-1.4...").
I'd like to display this PDF in a new browser window, but I'm having difficulty achieving this.
My research so far shows that I might be able to achieve what I want using a data: uri, so my code that's called when the ajax call completes is as follows:
function GotPDF(data)
{
// Here, data contains "%PDF-1.4 ..." etc.
var datauri = 'data:application/pdf;base64,' + Base64.encode(data);
var win = window.open("", "Your PDF", "width=1024,height=768,resizable=yes,scrollbars=yes,toolbar=no,location=no,directories=no,status=no,menubar=no,copyhistory=no");
win.document.location.href = datauri;
}
This causes a new browser window to open, but the contents are blank.
Interestingly, if I point my browser (IE9) at an existing file on my local disk by using a file: uri, such as file://c:/tmp/example.pdf, then I get the same result, i.e. a blank window.
Is there any way I can display this PDF data?
Code you wrote does not display anything, simply open a blank window (location.href is an hash for browsing history, not the content of the page).
To display a PDF you have, at least, following options:
× Embed the PDF viewer inside an object tag. It may not be as straightforward as you may imagine, take a look to this post for sample code. In short it should be something like this:
<object data="your_url_to_pdf" type="application/pdf">
<div>No PDF viewer available</div>
</object>
That's basic code but I suggest to follow what I say in the linked post if you need higher cross-browser compatibility (it also contains a few examples about how you might try to detect support for a PDF viewer).
× Download the file to local computer (simply add the full URL of your web service method that produces the file, do not forget to add the proper Content-Disposition in the header).
× Open the file into a new browser window. Create a normal a tag as you point to a PDF file on-line that you want to display in a new window. Change the href to javascript:functionName and in that function produce the URI you'll use to call the web service method.
Whatever you'll do, do not forget to set the proper MIME type in your response moreover you method shouldn't return a byte stream (even if encoded) but a valid response for your web browser.
If you are using <embed> or <object> tag to display a streamed PDF file (or other file types) from a server as in:
<object data="SomeServlet?do=get_doc&id=6" type="application/pdf" width="800" height="400">
make sure the server sends the proper http content-disposition value, which in this case would be inline.
I have an app that uses Javascript to perform some calculations and then plot the data, but I'd like to add the option for the user to be able to actually download the data into a csv or xls file.
Is there a way in Javascript (or some other method) to have the user press a button, then it will prompt them for the name of the file to save it as, and it will then create a comma-delimited or excel spreadsheet?
Thanks!
EDIT:
Thanks for all the suggestions everyone. Wish I could mark you all as answers, but upboats will have to do for now
It's not hard to open a window and write the csv into it. But I don't know of any way for javascript to change the Content-Type: header. And without that it won't prompt to save or open.
You'll need assistance from the server to do this. You can send the data to the server in a form variable and have the server send it right back with the correct header Content-type: text/csv you may also want the Content-Disposition: header to give your file a name.
Yes, but you'll need to use server-side code as well. Use JavaScript to construct a link to a page that streams the csv data back as an attachment. The server output should contain a content-disposition header of attachment; filename="fileName.csv".
No, you can't create and/or save a file directly from JavaScript. On some browsers/platforms (IE/Windows), you could create and write to a file via ActiveX object:
function WriteToFile()
{
var fso = new ActiveXObject("Scripting.FileSystemObject");
var s = fso.CreateTextFile("C:\\temp\\Test.txt", true);
s.WriteLine('Hello');
s.Close();
}
Another solution is to use client-side JavaScript (inside a browser) to output CSV data into a separate window (or pop-up) and have a user to copy/paste it into Excel.
If you want to do it in a browser website style it might be hard. But Javascript is a good language to do this, but you will need to use .hta instead of a normal .html. Creating an .hta creates a stand alone application just like a normal .exe.
Here is what you want to look for ActiveXObject("Excel.Application")
In order to transform a html into an hta, here is the tag
<HTA:APPLICATION
id="SomeId"
border="thin"
borderStyle="normal"
caption="yes"
maximizeButton="yes"
minimizeButton="yes"
showInTaskbar="yes"
windowState="yes"
innerBorder="yes"
navigable="yes"
scroll="auto"
scrollFlat="yes"
singleinstance="yes"
/>
For futher reading on hta and the excel active X
You could certainly write a browser plugin (ActiveX control on IE, NPAPI on others) with FireBreath that would do this; you'd have to write it in C++. Honestly, I agree with others in suggesting that you do this server-side instead, but you can do it with a plugin and it wouldn't be too difficult.
I think that it would be possible to do this (up to a certain size limit) with data URIs
I want to let the user download the current content of a textarea into a text file on their computer. In the past I would create an iframe pointing to a URL with the data, which would trigger a file download dialog. However this time the data is client side.
So, is it possible to let the user download data without sending it server side?
If it does not need to work in "old browsers" like IE, you could open a new window with the location 'data:text/plain,' + yourTextarea.value. The user can then save that file using the File/Page menu or Ctrl+S.
is it possible to let the user download data without sending it server side?
In the general case no.
It is possible with a data: URL, as in janmoesen's answer (although you should be URL-encoding the value to include in the URL, or you may face corruption when a %nn sequence appears in the data).
However, this becomes impractical for very long data, and it's not available in old browsers or IE. (IE8 allows short data URLs to be used for images, but not direct navigation.) So whilst you can include a data-URL method to enhance the process on browsers that support it, you will still need a fallback solution of sending a formful of the data to the server side and having it spit back a file in response.
(For security reasons, this should only be allowed in a POST request, and the server should include Content-Disposition: attachment in the response. A content-echo script can give you cross-site-scripting problems otherwise.)
Check out how File and Blob types work.
You can create one and trigger a download programmaticaly:
https://www.bennadel.com/blog/3472-downloading-text-using-blobs-url-createobjecturl-and-the-anchor-download-attribute-in-javascript.htm
https://blog.logrocket.com/programmatic-file-downloads-in-the-browser-9a5186298d5c/