javascript - Make a JSON POST request to server, receive a binary response (an Excel file), how to download it? - Stack Overflow

I'm trying to to make a POST call to server that sent JSON data to it. The server takes the JSON d

I'm trying to to make a POST call to server that sent JSON data to it. The server takes the JSON data, do some processing, then send back an Excel .xlsx as the response. I want the browser to open the "Save file as" dialog for the user to save. I have been looking for a clean solution to do this. But one possible solution in this question JavaScript/jQuery to download file via POST with JSON data suggest to save the Excel file on the server then send back a URL link, then open an iframe for user to download. This is a no-go for me, as the users can create thousands Excel files on the server and the server has limited saving spaces. I want the solution to be on-the-fly. Another solution I have seen suggested to convert data into form, then using form submit. Again this is a no-go, since my data is in the range of hundreds if not thousands of Excel rows.

My jQuery POST call:

   $.ajax({
          type: 'POST',
          url: '/server/path',
            data: JSON.stringify(dataSent),
            processData: false,
            success: function(data, textStatus, jqXHR) {

            },
            error: function(result, status, err) {

            },
            contentType: 'application/json',
            dataType: 'application/vnd.ms-excel'
        }); 

In the backend I set this :

Response.header("Content-Type", "application/vnd.ms-excel")

Response.header("Content-Disposition", "attachment; filename=\"export.xlsx\"")

What the best way to force the browser to open "Save file as ..." dialog ?

Thanks,

I'm trying to to make a POST call to server that sent JSON data to it. The server takes the JSON data, do some processing, then send back an Excel .xlsx as the response. I want the browser to open the "Save file as" dialog for the user to save. I have been looking for a clean solution to do this. But one possible solution in this question JavaScript/jQuery to download file via POST with JSON data suggest to save the Excel file on the server then send back a URL link, then open an iframe for user to download. This is a no-go for me, as the users can create thousands Excel files on the server and the server has limited saving spaces. I want the solution to be on-the-fly. Another solution I have seen suggested to convert data into form, then using form submit. Again this is a no-go, since my data is in the range of hundreds if not thousands of Excel rows.

My jQuery POST call:

   $.ajax({
          type: 'POST',
          url: '/server/path',
            data: JSON.stringify(dataSent),
            processData: false,
            success: function(data, textStatus, jqXHR) {

            },
            error: function(result, status, err) {

            },
            contentType: 'application/json',
            dataType: 'application/vnd.ms-excel'
        }); 

In the backend I set this :

Response.header("Content-Type", "application/vnd.ms-excel")

Response.header("Content-Disposition", "attachment; filename=\"export.xlsx\"")

What the best way to force the browser to open "Save file as ..." dialog ?

Thanks,

Share Improve this question edited May 23, 2017 at 10:33 CommunityBot 11 silver badge asked Feb 10, 2014 at 5:16 phamductriphamductri 1,3912 gold badges15 silver badges18 bronze badges 2
  • With the "Save as Excel on the server" approach, you're not supposed to leave the Excel file remaining on the server. Either delete the file as part of your process, or have some background process cleaning up periodically. No one expects that you leave 1000s of excel files on the server forever – jasonscript Commented Feb 10, 2014 at 6:37
  • Why do you assume a POST (not a GET) using a form is somehow more limited in the amount of data it can send than a Jquery POST ? Underneath, they're the same process. – Tim Williams Commented Feb 10, 2014 at 6:45
Add a ment  | 

3 Answers 3

Reset to default 0

I'm not sure there's a way to recieve binary data via JS and then initiate the download.

If I were tasked with this, I would change the method to a GET and generate the file (as a stream) and return it with the appropriate headers (Content-Disposition, Content-Length, Content-Type)

I figure out a way around this. Instead of making a POST call to force the browser to open the save dialog, I will make a POST call to generate the file, then temporary store the file on the server, return the filename . Then use a GET call for this file with "Content-Disposition: attachment; filename=filename1". The GET call with that header will force the browser to open the "Save this file" dialog, always.

This is actually very easy with Blob URLs.

First, download the file. I'll use fetch with async/await in TypeScript (you can always use promise chains instead of async/await and/or XHR instead of fetch):

(async () => {
   let response = await fetch("/post/path", {
       body: JSON.stringify(data), // must match 'Content-Type' header
       headers: {
           'content-type': 'application/json'
       },
       method: 'POST',
   });

   let blob = await response.blob();
   let filename = "file.txt";

   saveBlobAsFile(filename, blob); // This function is defined below
})();

Now that you have a blob, you can pass it to a function to download it by creating a Blob URL and a hidden link:

/**
 * Downloads a blob as a file.
 * 
 * TODO: Support iOS Safari, which doesn't support the "download" attribute.
 * 
 * @param name The name of the downloaded file
 * @param blob The blob to download
 */
export function saveBlobAsFile(name: string, blob: Blob) {
    // IE10 & IE11 Support, since they don't support the "download"
    // attribute on anchor tags.
    if (navigator.msSaveBlob) {
        navigator.msSaveBlob(blob, name);
        return;
    }

    // Create the URL and hidden anchor tag
    let $hiddenAnchorTag = $('<a style="display: none;"/>');
    let url = URL.createObjectURL(blob);

    // Set the attributes for the download
    $hiddenAnchorTag.attr('href', url);
    $hiddenAnchorTag.attr('download', name);

    // Insert the link and click to download
    $(document.body).append($hiddenAnchorTag);
    $hiddenAnchorTag[0].click();

    // Clean up after ourselves
    $hiddenAnchorTag.remove();
    URL.revokeObjectURL(url);
}

Other Notes

  • The fetch response object contains the headers, so you can parse the Content-Disposition to get the filename intended by the server. I found a couple good Regexes around the web that work pretty well for this. Mileage may vary, but I remend making a function for this and bounding it with some nice unit tests.
  • This works a lot better than trying to set the current location to the location of the file, because it allows you to include more details in the POST, including API keys or something similar for security, plus it allows you to handle errors/exceptions cleanly, and know when the operation is plete (such as warning on trying to navigate away from the page that the user is still waiting on a download).
  • Blobs even support slicing in data, so you could extend this to download large files by fetching the individual slices (yay Content-Range!) and assembling them into a single Blob, and downloading the final blob, all while giving the user a nice loading progress indicator!
  • You can use Blob URLs just like any other URLs. They point to the resource, so you could pass that URL to img tags, other libraries that ask for URLs, src tags, etc.
  • @phamductri Serving up temporary files on the server can be very dangerous! If you need to use that pattern, you'll want to abstract the filename using a table or lookup, so the user doesn't have control over the actual filenames or paths (use UUIDs in a specified directory), and make sure users can only download the files they generated. Just a few of things you need to ensure are as follows (this is not a prehensive list):
    • Users can't specify an arbitrary path to save to
      • They could save over your database configuration file, etc.
    • Users can't specify an arbitrary path to read to
      • They could read your database configuration file, etc.
    • File names can't conflict.
      • User A generates a file with the name "accounts_to_pay.csv". User B generates a file at the same time with the same name (either maliciously or accidentally), and now User A is paying whoever User B wants them to.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745292561a4620948.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信