Dart and Client Side File Handling (with authoriza

2019-03-02 12:29发布

A server side application requires authorization on file download links. This means a normal <a ng-href="{{createLinkToFile()}}"> is no longer sufficient to get enough parameters passed to the server.

When trying to use a programmatic call to the file download, I get the response data back to Dart client application. Using a simple http GET:

    var url = "http://example.com/file";
    headers.putIfAbsent("Authorization", () => "bearer " + token;
    _http.get(url: url, headers : headers);

The future returned by the GET will hold the data, but how do I instruct the browser to download it as a file, instead of just trying to keep it in memory?

Or is there a way to just do it in a normal link?

2条回答
男人必须洒脱
2楼-- · 2019-03-02 12:49

After downloading the data from the server like shown in Using Dart to Download a PNG File (Binary File) and displaying it not working you can create a download link like shown at http://blog.butlermatt.me/2014/03/dynamically-generating-download-files/

import 'dart:html';
void main() {
  List body = [ 'Some test data ...\n'];

  // Create a new blob from the data.
  Blob blob = new Blob(body, 'text/plain', 'native');
  // Create a data:url which points to that data.
  String url = Url.createObjectUrlFromBlob(blob);
  // Create a link to navigate to that data and download it.
  AnchorElement link = new AnchorElement()
      ..href = url
      ..download = 'random_file.txt'
      ..text = 'Download Now!';

  // Insert the link into the DOM.
  var p = querySelector('#text');
  p.append(link);
}
查看更多
Rolldiameter
3楼-- · 2019-03-02 12:59

The code of Seth solves indeed part of the problem. To make it a bit more complete, I'm now using the following:

void doPdfFileRequest(String url) {  
    var request = new HttpRequest();
    request.open('GET', url);
    request.responseType = "blob";
    request.withCredentials = false;
    request.setRequestHeader("Accept", _httpAcceptHeader);
    request.setRequestHeader("Authorization", "bearer " + token);
    request.onReadyStateChange
        .listen((r) => onData(request, "filename.pdf"));
    request.send();
}

void onData(HttpRequest request, String filename) {
    if (request.readyState == HttpRequest.DONE && request.status == 200) {
      if (!isIE()) {
        var contentType = request.getResponseHeader("content-type");

        AnchorElement downloadLink = new AnchorElement(
            href: Url.createObjectUrlFromBlob(request.response));
        downloadLink.rel = contentType;
        downloadLink.download = filename;

        var event = new MouseEvent("click", view: window, cancelable: false);
        downloadLink.dispatchEvent(event);
      } else {
        var href = Url.createObjectUrlFromBlob(request.response);
        window.open(href, "_self");
      }
   }
}

A few things to notice. Instead of using the downloadLink.click(), a mouse event is constructed to ensure that it works on Firefox as well as on Safari and Chrome. Firefox seems not to handle the click() otherwise. Binding it to the DOM as is done in the code of Seth isn't necessary.

Internet Explorer doesn't understand the download attribute, so nothing will happen, therefore a window.open is used to at least have it work (though not ideal) on IE, it's redirecting to self to avoid being hit by the pop up blocker.

There are solutions that convert the result download result to Base64 first and put it in a data:mimetype href, using the blob this isn't necessary.

A nice way to set the filename on the file to download would be through the content disposition header, but this header is marked as unsafe, so cannot be used. The filename is now set in the code.

Another note, notice that a HttpRequest is used instead http.get(), The HttpRequest allows you to set the responseType, in this case blob, which can be transformed into a object url.

查看更多
登录 后发表回答