16

I'd like to make an XML document in JavaScript then have a save dialog appear.

  1. It's OK if they have to click before the save can occur.
  2. It's *not* OK if I *have* to use IE to achieve this (I don't even need to support it at all). However, Windows is a required platform (so Firefox or Chrome are the preferred browsers if I can only do this in one browser).
  3. It's *not* OK if I need a web server. But conversely, I don't want to require the JavaScript to be run on a local file only, i.e. elevated privileges -- if possible. That is, I'd like to to run locally or on a *static* host. But just locally is OK.
  4. It's OK to have to bend over backwards to do this. The file won't be very big, but internet access might either be there, be spotty or just not be a possibility at all -- see (3).

So far the only ideas I have seen are to save the XML to an iframe and save that document -- but it seems that you can only do this in IE? Also, that I could construct a data URI and place that in a link. My fear here is that it will just open the XML file in the window, rather than prompt the user to save it.

I know that if I require the JavaScript to be local, I can raise privileges and just directly save the file (or hopefully cause a save dialog box to appear). However, I'd much prefer a solution where I do not require raised privileges (even a Firefox 3.6 only solution).

I apologize if this offends anyone's sensibilities (for example, not supporting every browser). I basically want to write an offline application and Javascript/HTML/CSS seem to be the best candidate considering the complexity of the requirements and the time available. However, I have this single requirement of being able to save data that must be overcome before I can choose this line of development.

Adam Luter
  • 2,193
  • 1
  • 15
  • 20
  • I've run a test with data uri's and if you set the mime type improperly, then you can force the user to save the data when clicking a link. The incredibly ugly caveat here is that you cannot specify a file name. And unless the browser by default asks you where and what name when saving downloads, it just saves as a huge ugly name in your downloads folder, which you have to find, and rename with an xml extension just to see. So, this is a non-solution. – Adam Luter Feb 09 '10 at 01:57
  • If you do not want to have the JS file as a local reference then where is it served from if a web server is not an option? – Jonathan Feb 19 '10 at 02:03
  • Why must the XML be created from a webpage? You could make a small, simple (no GUI) desktop app instead. – Bill Paetzke Feb 19 '10 at 02:10
  • @Jonathan, the idea is flexibility, in theory I could have the entire application as a single html file sitting anywhere, or at least a simple static collection of files sitting anywhere. So, it's that I don't want to give up the flexibility of having it on, say, a static web server (as much as I don't want a webserver itself be a requirement). But given the choice between the two, I have to choose "as a local file" because I cannot rely on network connectivity. – Adam Luter Feb 20 '10 at 02:30
  • @Bill I am not interested in developing in anything other than HTML/JS/CSS. I can easily choose other languages, platforms, etc. But given my resources and just my whimsy, that's my language and platform choice. – Adam Luter Feb 20 '10 at 02:31
  • @everyone I know it's a "security-concern" but I think that's BS. How is prompting the user to save a file less secure because JS created the content? The user is prompted either way for the location. I assume I'll have the same problem, then, trying to load a file from JS without a server in between (but that's a different question). If anything, it seems that requiring a 3rd party (a trip to the server) would decrease security (if you looked at this abstractly). So, concerning the bounty, if someone can convince me why I can't, you can have the bounty, otherwise I'll award on best-effort. – Adam Luter Feb 20 '10 at 02:37
  • Maybe, as a workaround, put the generated javascript into a textfield and ask the user to copy it? – jedierikb Feb 20 '10 at 04:06
  • File API landed in Firefox 3.6 http://hacks.mozilla.org/2009/12/w3c-fileapi-in-firefox-3-6/ , but I can't see anything to save files. Just to read it. – NVI Feb 20 '10 at 22:56
  • @NV Yeah that's good for reading the file and is probably how I will do reading -- but it won't let me save them :( . – Adam Luter Feb 21 '10 at 00:24
  • Thanks everyone for their help. – Adam Luter Feb 21 '10 at 13:15

9 Answers9

5

How about this downloadify script?

Which is based on Flash and jQuery, which can prompt you dialog box to save file in your computer.

Downloadify.create('downloadify',{
  filename: function(){
    return document.getElementById('filename').value;
  },
  data: function(){ 
    return document.getElementById('data').value;
  },
  onComplete: function(){ 
    alert('Your File Has Been Saved!'); 
  },
  onCancel: function(){ 
    alert('You have cancelled the saving of this file.');
  },
  onError: function(){ 
    alert('You must put something in the File Contents or there will be nothing to save!'); 
  },
  swf: 'media/downloadify.swf',
  downloadImage: 'images/download.png',
  width: 100,
  height: 30,
  transparent: true,
  append: false
});
YOU
  • 120,166
  • 34
  • 186
  • 219
  • 1
    Flash is the only way I've ever seen to do what you are asking. This answer (jQuery / event driven) is a very elegant solution. – Jonathan Julian Feb 20 '10 at 14:03
  • Yeah this is probably going to be the only answer to my question -- even if I don't *want* to rely on flash. (I didn't say so up there though :P ). I can always rely on flash *or* elevated privileges. S.Mark, do you know if there is an equivalent for loading a file via javascript? (Again, of course, prompting the user). – Adam Luter Feb 20 '10 at 14:44
  • If someone doesn't mind clarifying this point -- I work *with* flash developers, but never with flash myself -- the site says it's compiled for online use only. I assume that I could get it compiled to work locally too? – Adam Luter Feb 20 '10 at 14:48
  • @Adam - Yea, you could compile for local usage - but not "too". In the "Publish Settings" dialog, Flash asks if you want "Network Access" only, or "Local Access" only. Unless we are not referring to the same thing. – Moshe Feb 21 '10 at 03:58
  • @Moshe, Thanks for the tidbit -- I've never worked *in* flash before. That'd be *OK* since I could do network access, and then rely on elevated privileges for local access. But it means I'll probably just start with elevated privileges and only do flash when I'm done and want it to work on a static web host. – Adam Luter Feb 21 '10 at 13:14
4

Using a base64 encoded data URI, this is possible with only html & js. What you can do is encode the data that you want to save (in your case, a string of XML data) into base64, using a js library like jquery-base64 by carlo. Then put the encoded string into a link, and add your link to the DOM.

Example using the library I mentioned (as well as jquery):

<html>
<head>
    <title>Example</title>
</head>
<body>
    <script>
        //include jquery and jquery-base64 here (or whatever library you want to use)
        document.write('<a href="data:application/octet-stream;base64;charset=utf-8,' + $.base64.encode( "this is a example, which requires the jquery-base64 library to work... replace this text with your xml" ) + '">click to make save dialog</a>');
    </script>
</body>
</html>

...and remember to make the content-type something like application/octet-stream so the browser doesn't try to open it.

Warning: some older IE versions don't support base64, but you said that didn't matter, so this should work fine for you.

slang
  • 626
  • 7
  • 26
  • This is great. Is there any way you know to have it save to the desktop as an actual file? For example, pushing XML through this and and having it download to the users page as 'text.xml'? Right now you can pass any string and it saves as 'download' with the text inside. – streetlight Nov 14 '12 at 13:07
  • Using this answer, it can be saved to the desktop as an actual file, but I haven't found a way to set the file name. What you actually should be using is the [HTML5 Filesystem API](http://updates.html5rocks.com/2011/08/Saving-generated-files-on-the-client-side)... this will allow you to set a specific file name. – slang Nov 15 '12 at 14:01
2

Without any more insight into your specific requirements, I would not recommend a pure Javascript/HTML solution. From a user perspective you would probably get the best results writing a native application. However if it will be faster to use Javascript/HTML, I recommend using a local application hosting a lightweight web server to serve up your content. That way you can cleanly handle the file saving server-side while focusing the bulk of your effort on the front-end application.

You can code up a web server in - for example - Python or Ruby using very few lines of code and without 3rd party libraries. For example, see:


"""
Serves files out of its current directory.
Doesn't handle POST requests.
"""
import SocketServer
import SimpleHTTPServer

PORT = 8080

def move():
    """ sample function to be called via a URL"""
    return 'hi'

class CustomHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        #Sample values in self for URL: http://localhost:8080/jsxmlrpc-0.3/
        #self.path  '/jsxmlrpc-0.3/'
        #self.raw_requestline   'GET /jsxmlrpc-0.3/ HTTP/1.1rn'
        #self.client_address    ('127.0.0.1', 3727)
        if self.path=='/move':
            #This URL will trigger our sample function and send what it returns back to the browser
            self.send_response(200)
            self.send_header('Content-type','text/html')
            self.end_headers()
            self.wfile.write(move()) #call sample function here
            return
        else:
            #serve files, and directory listings by following self.path from
            #current working directory
            SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)

httpd = SocketServer.ThreadingTCPServer(('localhost', PORT),CustomHandler)

print "serving at port", PORT
httpd.serve_forever()

Finally - Depending on who will be using your application, you also have the option of compiling a Python program into a Frozen Binary so the end user does not have to have Python installed on their machine.

Justin Ethier
  • 131,333
  • 52
  • 229
  • 284
  • Thank you Justin for your very thorough reply. My ideal here is that the file could be a local html (or local folder of html, etc.) *or* on a static host (cheap) when there is net. Based on the resources I have, I do not want to develop a "real" app. (But if I did, I'd probably pick Ruby or Python). Freezing Python is a big plus if I broke down. But I really just want to stick to a static html/js/css "web" app (minus the web). The application is a "basic enter data, then a guided process for composing it". The UI will be pretty basic and I just need to save/load state as XML. – Adam Luter Feb 20 '10 at 02:43
  • You're welcome. I think if you are planning to go with a html/js/css solution, however, you will still want a back-end - albeit a minimalist one. – Justin Ethier Feb 20 '10 at 15:29
1

Javascript is not allowed to write to a local machine. Your question is similar to this one.

I suggest creating a simple desktop app.

Community
  • 1
  • 1
Bill Paetzke
  • 13,332
  • 6
  • 44
  • 46
  • 4
    Thanks Bill, I know this is the standard answer, I'd like some rational why the DOM thinks that `Click` is secure enough to prompt the user to save the file (and in many of today's browser's it doesn't even prompt...). But if I used JS to create the txt file say some psuedo-code like: `Click`, this is somehow inherently less secure? Theoretically, it should be less secure to have to ask the network for the data again, since this isn't a secure connection (http vs https). – Adam Luter Feb 20 '10 at 02:50
0

Why not use a hybrid flash for client and some server solution server-side. Most people have flash so you can default to client side to conserve resources on the server.

jwize
  • 4,230
  • 1
  • 33
  • 51
0

Are you looking for something like this?

If PHP is ok, if would be much easier.

tr4656
  • 1,298
  • 9
  • 16
  • Thank you tr4656, no I am already familiar with the XML api available in JS. I am trying to get that xml saved to the local disk *through a user prompted save dialog*. Similarly (and this is a separate question), I am also wanting to prompt the user to load a file (but again without a web server involved). Barring any of that, I'll settle for a convincing argument (even a historical one) on why it's less secure (rather than just having to believe it). – Adam Luter Feb 20 '10 at 02:53
  • @Adam - I agree with you that this whole security argument is somewhat bogus or at the very least, unexplained. – Moshe Feb 21 '10 at 04:03
0

Is localhost PHP server ok? Web traditionally can't save to hard drive because of security concerns. PHP can push files though it requires a server.

Print to PDF plugins are available for available for all browsers. Install once, print to PDF forever. Then, you can use a javascript or Flash to call a Print function.

Also, if you are developing for an environment where internet access is spotty, conwider using VB.NET or some other desktop language.

EDIT:

You can use the browser's Print function.

Moshe
  • 57,511
  • 78
  • 272
  • 425
  • Moshe, thank you for your reply. (read my other comments on using a webserver, local or otherwise, and also about using something other than html/js/css). I *have* to use XML, it's just a requirement (and not a really horrible one considering the data). But I didn't mention in my question (because it was a separate question) that I need to be able to load the same XML file later too (so, printing to PDF would be great, except I'd not be able to load from it). Using the print function is clever, but I don't think it would work for XML data. – Adam Luter Feb 20 '10 at 02:57
  • @Adam - I don't see anything wrong with XML. Why wouldn't print work for XML data? You can output the XML as a plain text to the browser. Loading the XML can be done either with Flash or with jQuery as XML can be loaded into the browser like any other (text/html) file can. – Moshe Feb 21 '10 at 04:02
  • Guh, I hate AJAX comment boxes, because I always end up losing the contents after finishing a box and then having an accident (like hitting backspace after hitting tab accidentally). Anyway -- I was saying I think the print+XML route is, at the least, too difficult for a user. Maybe even worse than saying "hey click here, then choose save/as, then click back" (if I went the route where I could bring the XML up as it's own page). I just want it to be a normal save/load routine for the user's sanity/sake. (So, it looks like Flash could help me out, or elevated privileges). – Adam Luter Feb 21 '10 at 13:11
0

With IE you could use document.execCommand, but I note that IE is not an option.

Here's something that looks like it might help, although it will not prompt with SaveAs dialog, https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FOL.

Jonathan
  • 5,953
  • 1
  • 26
  • 35
  • Jonathan, your link is broken, but my guess it's the elevated privilege code I mention in the 2nd lower paragraph. Thanks though! :) – Adam Luter Feb 20 '10 at 14:42
0

One simple but odd way to do this that doesn't require any Flash is to create an <a/> with a data URI for its href. This even has pretty good cross-browser support, although for IE it must be at least version 8 and the URI must be < 32k. It looks like someone else on SO has more to say on the topic.

Community
  • 1
  • 1
Jacob
  • 77,566
  • 24
  • 149
  • 228
  • Yeah but as I note in my comment to the question, a data uri won't work in the case of wanting any sort of sane filename. – Adam Luter Feb 21 '10 at 00:23
  • As a small update: At least in Chrome, we can set "sane" filenames using the download property in , which makes working with URIs a little more feasible... – Richard Ambler Aug 03 '13 at 09:17