Rectangle 27 0

javascript Download a file by jQuery.Ajax?


$.fileDownload('some/file.pdf')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

@StanE thanks for the comment. I updated the bottom of my answer which clarifies some better options now that it is 2016 (and jQuery was almost a given back when I wrote this plugin and answered this question -- which is certainly not the case anymore)

Bluish is completely right about this, you can't do it through Ajax because JavaScript cannot save files directly to a user's computer (out of security concerns). Unfortunately pointing the main window's URL at your file download means you have little control over what the user experience is when a file download occurs.

Depending on what browsers you need to support you may be able to use https://github.com/eligrey/FileSaver.js/ which allows more explicit control than the IFRAME method jQuery File Download uses.

Here is a simple use case demo using the plugin source with promises. The demo page includes many other, 'better UX' examples as well.

I agree, a blog is a far better place to place a lengthy description of how to use your plugin and how it works. but you could have at least gave a short overview of how this plugin solves the problem. For example, this solves the problem by having the server set a cookie and having your javascript continuously look for the cookie until it exists. Once it exists, we can assume that the download is complete. With that kind of information one could easily roll their own solution very quickly, and the answer no longer relies 100% on your blog/plugin/jquery and can be applied to other libraries.

I created jQuery File Download which allows for an "Ajax like" experience with file downloads complete with OnSuccess and OnFailure callbacks to provide for a better user experience. Take a look at my blog post on the common problem that the plugin solves and some ways to use it and also a demo of jQuery File Download in action. Here is the source

I love what you built but I suspect that to get more StackOverFlow credit your answer here should contain a bit more detail. Specifically on how you solved the problem.

Royi, as I understand it AJAX can never support file downloads that result in a file download popup to save to disk. Have you found a way that I'm unaware of?

it would be nice if you would mention exactly how this "plugin" gets around the limitation, rather than forcing us to go to your blog/plugin source to see it. for example, is it instead posting to an iframe? is it instead requiring the remote script to save the file and return a url to it?

Note
Rectangle 27 0

javascript Download a file by jQuery.Ajax?


AJAX
function base64ToBlob(base64, mimetype, slicesize) {
    if (!window.atob || !window.Uint8Array) {
        // The current browser doesn't have the atob function. Cannot continue
        return null;
    }
    mimetype = mimetype || '';
    slicesize = slicesize || 512;
    var bytechars = atob(base64);
    var bytearrays = [];
    for (var offset = 0; offset < bytechars.length; offset += slicesize) {
        var slice = bytechars.slice(offset, offset + slicesize);
        var bytenums = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            bytenums[i] = slice.charCodeAt(i);
        }
        var bytearray = new Uint8Array(bytenums);
        bytearrays[bytearrays.length] = bytearray;
    }
    return new Blob(bytearrays, {type: mimetype});
};
var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
    // Do it the HTML5 compliant way
    var blob = base64ToBlob(result.download.data, result.download.mimetype);
    var url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = result.download.filename;
    a.click();
    window.URL.revokeObjectURL(url);
}
{
    result: 'OK',
    download: {
        mimetype: string(mimetype in the form 'major/minor'),
        filename: string(the name of the file to download),
        data: base64(the binary data as base64 to download)
    }
}

You can with HTML5

NB: The file data returned MUST be base64 encoded because you cannot JSON encode binary data

That means that I can do the following to save a file via AJAX

The function base64ToBlob was taken from here and must be used in compliance with this function

This is good if your server is dumping filedata to be saved. However, I've not quite worked out how one would implement a HTML4 fallback

Note
Rectangle 27 0

javascript Download a file by jQuery.Ajax?


window.location="download.action?para1=value1...."

Nice one...as I was struggling with handling the download file prompt and using jquery ajax..and this solution works perfectly for me ..+1

Note that this requires the server to be setting a Content-Disposition header value of 'attachment', otherwise the browser will redirect to (and display) the response content

Or alternatively use window.open(<url>, '_blank'); to ensure that the download won't replace your current browser content (regardless of the Content-Disposition header).

The problem with this solution is that if the operation fails/the server returns an error, your page will be redirected to the error page. To solve that use the iFrame solution

this is good solution but i want to show loading bar while downloading. and this is not possible with same mechanism.

Note
Rectangle 27 0

javascript Download a file by jQuery.Ajax?


$.fileDownload('some/file.pdf')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

@StanE thanks for the comment. I updated the bottom of my answer which clarifies some better options now that it is 2016 (and jQuery was almost a given back when I wrote this plugin and answered this question -- which is certainly not the case anymore)

Bluish is completely right about this, you can't do it through Ajax because JavaScript cannot save files directly to a user's computer (out of security concerns). Unfortunately pointing the main window's URL at your file download means you have little control over what the user experience is when a file download occurs.

Depending on what browsers you need to support you may be able to use https://github.com/eligrey/FileSaver.js/ which allows more explicit control than the IFRAME method jQuery File Download uses.

Here is a simple use case demo using the plugin source with promises. The demo page includes many other, 'better UX' examples as well.

I agree, a blog is a far better place to place a lengthy description of how to use your plugin and how it works. but you could have at least gave a short overview of how this plugin solves the problem. For example, this solves the problem by having the server set a cookie and having your javascript continuously look for the cookie until it exists. Once it exists, we can assume that the download is complete. With that kind of information one could easily roll their own solution very quickly, and the answer no longer relies 100% on your blog/plugin/jquery and can be applied to other libraries.

I created jQuery File Download which allows for an "Ajax like" experience with file downloads complete with OnSuccess and OnFailure callbacks to provide for a better user experience. Take a look at my blog post on the common problem that the plugin solves and some ways to use it and also a demo of jQuery File Download in action. Here is the source

I love what you built but I suspect that to get more StackOverFlow credit your answer here should contain a bit more detail. Specifically on how you solved the problem.

Royi, as I understand it AJAX can never support file downloads that result in a file download popup to save to disk. Have you found a way that I'm unaware of?

it would be nice if you would mention exactly how this "plugin" gets around the limitation, rather than forcing us to go to your blog/plugin source to see it. for example, is it instead posting to an iframe? is it instead requiring the remote script to save the file and return a url to it?

Note
Rectangle 27 0

javascript Download a file by jQuery.Ajax?


AJAX
function base64ToBlob(base64, mimetype, slicesize) {
    if (!window.atob || !window.Uint8Array) {
        // The current browser doesn't have the atob function. Cannot continue
        return null;
    }
    mimetype = mimetype || '';
    slicesize = slicesize || 512;
    var bytechars = atob(base64);
    var bytearrays = [];
    for (var offset = 0; offset < bytechars.length; offset += slicesize) {
        var slice = bytechars.slice(offset, offset + slicesize);
        var bytenums = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            bytenums[i] = slice.charCodeAt(i);
        }
        var bytearray = new Uint8Array(bytenums);
        bytearrays[bytearrays.length] = bytearray;
    }
    return new Blob(bytearrays, {type: mimetype});
};
var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
    // Do it the HTML5 compliant way
    var blob = base64ToBlob(result.download.data, result.download.mimetype);
    var url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = result.download.filename;
    a.click();
    window.URL.revokeObjectURL(url);
}
{
    result: 'OK',
    download: {
        mimetype: string(mimetype in the form 'major/minor'),
        filename: string(the name of the file to download),
        data: base64(the binary data as base64 to download)
    }
}

You can with HTML5

NB: The file data returned MUST be base64 encoded because you cannot JSON encode binary data

That means that I can do the following to save a file via AJAX

The function base64ToBlob was taken from here and must be used in compliance with this function

This is good if your server is dumping filedata to be saved. However, I've not quite worked out how one would implement a HTML4 fallback

Note
Rectangle 27 0

javascript Download a file by jQuery.Ajax?


window.location="download.action?para1=value1...."

Nice one...as I was struggling with handling the download file prompt and using jquery ajax..and this solution works perfectly for me ..+1

Note that this requires the server to be setting a Content-Disposition header value of 'attachment', otherwise the browser will redirect to (and display) the response content

Or alternatively use window.open(<url>, '_blank'); to ensure that the download won't replace your current browser content (regardless of the Content-Disposition header).

The problem with this solution is that if the operation fails/the server returns an error, your page will be redirected to the error page. To solve that use the iFrame solution

this is good solution but i want to show loading bar while downloading. and this is not possible with same mechanism.

Note
Rectangle 27 0

javascript Download a file by jQuery.Ajax?


AJAX
function base64ToBlob(base64, mimetype, slicesize) {
    if (!window.atob || !window.Uint8Array) {
        // The current browser doesn't have the atob function. Cannot continue
        return null;
    }
    mimetype = mimetype || '';
    slicesize = slicesize || 512;
    var bytechars = atob(base64);
    var bytearrays = [];
    for (var offset = 0; offset < bytechars.length; offset += slicesize) {
        var slice = bytechars.slice(offset, offset + slicesize);
        var bytenums = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            bytenums[i] = slice.charCodeAt(i);
        }
        var bytearray = new Uint8Array(bytenums);
        bytearrays[bytearrays.length] = bytearray;
    }
    return new Blob(bytearrays, {type: mimetype});
};
var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
    // Do it the HTML5 compliant way
    var blob = base64ToBlob(result.download.data, result.download.mimetype);
    var url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = result.download.filename;
    a.click();
    window.URL.revokeObjectURL(url);
}
{
    result: 'OK',
    download: {
        mimetype: string(mimetype in the form 'major/minor'),
        filename: string(the name of the file to download),
        data: base64(the binary data as base64 to download)
    }
}

You can with HTML5

NB: The file data returned MUST be base64 encoded because you cannot JSON encode binary data

That means that I can do the following to save a file via AJAX

The function base64ToBlob was taken from here and must be used in compliance with this function

This is good if your server is dumping filedata to be saved. However, I've not quite worked out how one would implement a HTML4 fallback

Note
Rectangle 27 0

javascript Download a file by jQuery.Ajax?


@RequestMapping(value = "/rootto/my/xlsx", method = RequestMethod.GET)
public void downloadExcelFile(@RequestParam(value = "param1", required = false) String param1,
    HttpServletRequest request, HttpServletResponse response)
            throws ParseException {

    Workbook wb = service.getWorkbook(param1);
    if (wb != null) {
        try {
            String fileName = "myfile_" + sdf.format(new Date());
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
            wb.write(response.getOutputStream());
            response.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    }
var iframe = document.createElement('iframe');
iframe.id = "IFRAMEID";
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.src = 'SERVERURL'+'?' + $.param($scope.filtro);
iframe.addEventListener("load", function () {
     console.log("FILE LOAD DONE.. Download should start now");
});

Here is a quick fiddle jsfiddle.net/y2xezyoj this fires the load event ass soon as the pdf file is loaded into the iframe.. this fiddle does not download becaus the key for downloading is in the server side "response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\"");"

Here is what I did, pure javascript and html. Did not test it but this should work in all browsers.

Using just components that is supported in all browsers no additional libraries.

You are totally right load event is fired right after the server is done processing starts sending the file. This is what i was looking for, 1- block the button and show processing so that the user can have a feedback that things are happening. 2 - Then when the server is done processing and about to send the file 3- (load event is fired) where I unlock the button and remove processing spinner 4 - the user is now poped up with save file or the browser starts download it in the defined download location. Sorry my English.

it seems your load event is not called for Content-disposition attachment content (because nothing is loaded into the iframe), if it works for you (you get the console.log) pls post a sample

yes it will work in that case, but if the file is downloaded, that is the server sends Content-Disposition: attachment, then the load event will not fire which was my point

Note
Rectangle 27 0

javascript Download a file by jQuery.Ajax?


# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
$("#my-button").on("click", function(){

// Data to post
data = {
    ids: [1, 2, 3, 4, 5]
};

// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        // Trick for making downloadable link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Give filename you wish to download
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    }
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});
  • After fetching content as a blob(binary), we are creating a downloadable URL and attaching it to invisible "a" link then clicking it. I did a POST request here. Instead, you can go for a simple GET too. We cannot download the file through Ajax, must use XMLHttpRequest.
  • Posting an array as JSON to the server using XMLHttpRequest.

Here we need to carefully set few things on the server side. I set few headers in Python Django HttpResponse. You need to set them accordingly if you use other programming languages.

I faced the same issue and successfully solved it. My use-case is this.

Since I download xls(excel) here, I adjusted contentType to above one. You need to set it according to your file type. You can use this technique to download any kind of files.

The above snippet is just doing following

Note
Rectangle 27 0

javascript Download a file by jQuery.Ajax?


# GET /files/{:id}/download_file/
def download_file
    send_file(@file.file,
          :disposition => 'attachment',
          :url_based_filename => false)
end
function download_file(file_id) {
  let url       = '/files/' + file_id + '/download_file';
    $.ajax({
    type: 'GET',
    url: url,
    processData: false,
    success: function (data) {
       window.location = url;
    },
    error: function (xhr) {
     console.log(' Error:  >>>> ' + JSON.stringify(xhr));
    }
   });
 }

The trick is the window.location part. The controller's method looks like:

Note