15

I have the following code to download a .csv file:

$.ajax({
    url: urlString,
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    cache: false,
    success: function(data) {
        if (data) {
            var iframe = $("<iframe/>").attr({
                src: data,
                style: "visibility:hidden;display:none"
            }).appendTo(buttonToDownloadFile);
        } else {
            alert('Something went wrong');
        }
    }
});

The urlString is pointing to a Restful service that generates the .csv file and returns the file path which is assigned to the src attribute for the iFrame. This works for any .csv files but I'm having problems with .xml files.

When I use the same code but changing the contentType to text/xml and use it for downloading .xml files this doesn't work.

Can I use the same approach here for .xml files?

UPDATE:

Thanks to Ben for pointing me to the right direction. It turns out I don't need the ajax call at all. Instead, I can just use the iFrame and its url attribute to call the web service, which will generate the content, add the header (Content-Disposition), and return the stream.

Narendra Jadhav
  • 10,052
  • 15
  • 33
  • 44
notlkk
  • 1,231
  • 2
  • 23
  • 40

2 Answers2

19

You can also offer it as a download from a virtual anchor element, even if the data is client-side:

/*
 * Create an anchor to some inline data...
 */

var url = 'data:application/octet-stream,Testing%20one%20two%20three';
var anchor = document.createElement('a');
    anchor.setAttribute('href', url);
    anchor.setAttribute('download', 'myNote.txt');

/*
 * Click the anchor
 */

// Chrome can do anchor.click(), but let's do something that Firefox can handle too

// Create event
var ev = document.createEvent("MouseEvents");
    ev.initMouseEvent("click", true, false, self, 0, 0, 0, 0, 0, false, false, false, false, 0, null);

// Fire event
anchor.dispatchEvent(ev);

http://jsfiddle.net/D572L/

Redsandro
  • 11,060
  • 13
  • 76
  • 106
  • 2
    Brilliant. This just helped me with a dilemma (outputting a generated PDF using $.ajax, so that you can display a loader on clicking the download link, and then hiding it again once the file is offered to the browser. Thanks!! – Klaas Leussink Jul 22 '14 at 13:37
  • 1
    Thanks! It works on ios in opposite to hidden iframe. – Lucas Dec 03 '14 at 12:43
16

I'm guessing that the problem is that most browsers will try to render XML in the browser itself, whereas they tend to have no handler for CSV, so they'll automatically default to prompt the user to download the file. Try modifying the headers of the XML file to force the download. Something like (PHP example):

header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header('Content-Disposition: attachment; filename="some filename"');

That should tell most browsers not to attempt to open the file, but instead to have the user download the file and let the OS determine what to do with it.

If you have no power to control headers in the XML file itself, you can try a work-around using a server-side script. Use JS to pass the URL to a server-side script:

//build the new URL
var my_url = 'http://example.com/load_file_script?url=' + escape(path_to_file);
//load it into a hidden iframe
var iframe = $("<iframe/>").attr({
    src: my_url,
    style: "visibility:hidden;display:none"
}).appendTo(buttonToDownloadFile);

and on the server-side (your http://example.com/load_file_script script) you use cURL/file_get_contents/wgets/[some other mechanism of fetching remote files] to grab the contents of the remote file, add the Content-Disposition: attachment headers, and print the code of the original file.

Narendra Jadhav
  • 10,052
  • 15
  • 33
  • 44
Ben D
  • 14,321
  • 3
  • 45
  • 59
  • I'm afraid I don't quite understand the work-around you suggested. My server is .NET (WCF). Are you saying that I should add the headers from the server side? – notlkk May 28 '13 at 20:18
  • Yeah. If you don't control the RESTful API, then there is no way for you to set headers using just HTML/JS. So what you do is create a script on your .NET server that can receive the remote URL as an argument, go fetch the remote file, re-package it with new headers, and then serve it as an attachment. – Ben D May 28 '13 at 20:24
  • I see. Do I need all 4 headers as you pointed out? And what content type should I use in my javascript? – notlkk May 28 '13 at 20:34
  • 1
    No, not necessarily. The `Content-Disposition: attachment; filename="some filename"` is probably the only one you need, but some browsers can be finicky. – Ben D May 28 '13 at 20:47