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.

10
  • 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. Commented Feb 9, 2010 at 1: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? Commented Feb 19, 2010 at 2:03
  • Why must the XML be created from a webpage? You could make a small, simple (no GUI) desktop app instead. Commented Feb 19, 2010 at 2: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. Commented Feb 20, 2010 at 2: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. Commented Feb 20, 2010 at 2:31

9 Answers 9

5
+550

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
});
Sign up to request clarification or add additional context in comments.

5 Comments

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.
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).
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 - 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, 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.
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.

2 Comments

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.
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... this will allow you to set a specific file name.
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.

2 Comments

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.
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.
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.

1 Comment

Thanks Bill, I know this is the standard answer, I'd like some rational why the DOM thinks that <a href="mydoc.txt">Click</a> 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: <a onclick="window.promptSave('It was a dark and stormy night...', 'text/plain', 'mydoc.txt')">Click</a>, 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).
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.

3 Comments

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 - 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.
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).
0

Are you looking for something like this?

If PHP is ok, if would be much easier.

2 Comments

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 - I agree with you that this whole security argument is somewhat bogus or at the very least, unexplained.
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.

1 Comment

Jonathan, your link is broken, but my guess it's the elevated privilege code I mention in the 2nd lower paragraph. Thanks though! :)
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.

2 Comments

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.
As a small update: At least in Chrome, we can set "sane" filenames using the download property in <a/>, which makes working with URIs a little more feasible...
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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.