How to show save file dialog in Safari?

2019-02-19 10:54发布

问题:

I need help. I have an angular app and by using DocRaptor want to generate PDF and save it as file. But I cant trigger the dialog to save file in Safari with any method what I have found on Stack Overflow. Those methods open file in current browser tab and replace site html or open file in new tab. No one cant shows the dialog. Here the examples what I have already tried to use. Environment MacOS - EL Capitan. Safari 9.0.3

Solution #1

var content = 'file content for example';
var blob = new Blob([ content ], { type : 'text/plain' });
$scope.url = (window.URL || window.webkitURL).createObjectURL( blob );

Example jsfiddle. Shows file in current tab. Replaces site. But works in Chrome.

Solution #2

<a target="_self" href="mysite.com/uploads/ahlem.pdf" download="foo.pdf">

Example jsfiddle. Doesnt work at all in Safari. Works in Chrome.

Solution #3

<a class="btn" ng-click="saveJSON()" ng-href="{{ url }}">Export to JSON</a>

and

$scope.saveJSON = function () {
            $scope.toJSON = '';
            $scope.toJSON = angular.toJson($scope.data);
            var blob = new Blob([$scope.toJSON], { type:"application/json;charset=utf-8;" });           
            var downloadLink = angular.element('<a></a>');
                        downloadLink.attr('href',window.URL.createObjectURL(blob));
                        downloadLink.attr('download', 'fileName.json');
            downloadLink[0].click();
        };

Example Code Snippet. Shows the file content instead of document's html.

Solution #4

function download(text, name, type) {
  var a = document.getElementById("a");
  var file = new Blob([text], {type: type});
  a.href = URL.createObjectURL(file);
  a.download = name;
}

Example Code Snippet. Replace document with file content in Safari. Works in Chrome.

And similar Solution #5

function download(text, name, type) {
    var a = document.createElement("a");
    var file = new Blob([text], {type: type});
    a.href = URL.createObjectURL(file);
    a.download = name;
    a.click();
}

Example jsfiddle. Doesnt work at all in Safari. Works in Chrome.

Also I have tried to use libraries like: FileSaver - It opens file in Safari instead of document. So you should click Cmd+S. Example. If we use type 'pplication/octet-stream' the name of file will be unknown or there was be an error 'Failed to load resource: Frame load interrupted'. Issue.

Second library Downloadify - doesnt work in Safari at all. Issue.

Angular library nw-fileDialog - instead of save as it shows choose file. Issue.

DocRaptor has own example with jQuery. Example with angular in jsfiddle. It works in Chrome but in Safari example doesnt work be cause of error with SAMEORIGIN

Refused to display 'https://docraptor.com/docs' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'.

But if we reproduce it on server and change url on 'https://docraptor.com/docs.pdf' it works and open file in new tab and automatically download the file so you cant choose a folder and after download user see white empty screen tab in browser. If we specify form target="_self" it will work perfect, but console will have an error 'Failed to load resource:'.

I will appreciate any help with this problem. Thanks. Regards.

回答1:

Try using Blob file for this:

// Buffer can be response from XMLHttpRequest/Ajax or your custom Int32 data
function download(buffer, filename) {
  var file = new Blob([buffer], {
    type: 'application/octet-stream' // Replace your mimeType if known
  });
  var fileReader = new FileReader();

  fileReader.onloadend = function(e) {
    var converted = e.target.result;
    converted.name = filename;
    converted.webkitRelativePath = filename;

    var iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
    iframe.src = converted;
  };
  fileReader.onerror = function(e) {
    throw new Error('Something is wrong with buffer data');
  };
  fileReader.file = file;
  fileReader.readAsDataURL(file);
}

It basically uses filebuffer and download that as an iframe content. Make sure to hook correct mime type so that safari security system will recieved analyse filetype.



回答2:

Ideally, Solution #2 would be the answer, but the download attribute does not yet have cross-browser support.

So you have to use a <form> to create the download. As you noted, DocRaptor's jQuery example uses this technique.

The SAMEORIGIN error is actually because JSFiddle is running the code in an iFrame with their origin settings. If you run this straight from your Angular application, you shouldn't have any problems.