Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter WebView blob pdf download

I am trying to download a file from a website using flutter, I first used evaluateJavaScript and changed some values before clicking a generate button which is supposed to download an appropriate pdf file to the phone.

Here is my code:

InAppWebView(
        initialUrl: '...',
        initialOptions: InAppWebViewGroupOptions(
          crossPlatform: InAppWebViewOptions(
              debuggingEnabled: true,
              useOnDownloadStart: true,
              javaScriptEnabled: true,
          ),
        ),
        //javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (InAppWebViewController webViewController) {
          _controller = webViewController;
          },
          onLoadStop: (_controller ,url) async {
          await _loadData();

          _controller.evaluateJavascript(source:
"console.log(document.getElementById('field-lastname').value = '${data[Field.name]}' );"
+"console.log(document.getElementById('generate-btn').click());"

           );
          },

          onDownloadStart:(_controller,url) async{
            print("onDownloadStart $url");
            final taskId = await FlutterDownloader.enqueue(
                url: url,
                savedDir: (await getExternalStorageDirectory()).path,
            showNotification: true, // show download progress in status bar (for Android)
            openFileFromNotification: true,);
          },

      ),

The URL printed is like this one

onDownloadStart blob:https://example.com/a2a5316c-9290-4da7-8a2a-9f899861046a

And here is the console:
And here is the console

Can someone help me?

like image 745
Quantum Martin Avatar asked Nov 16 '20 21:11

Quantum Martin


People also ask

How do I add PDF to Webview flutter?

If possible, you can display PDF documents and visit online websites using the add-in: import 'dart:html' as html; html. window. open('http:///www.website.com/document.pdf');

How do I enable downloads in flutter Webview?

It can recognize downloadable files in both Android (using setDownloadListener ) and iOS platforms! To be able to recognize downloadable files, you need to set the useOnDownloadStart: true option, and then you can listen the onDownloadStart event! Then, you need to ask permission using the permission_handler plugin.


2 Answers

Downloading file with blob url is tricky and not supported out of the box in the current state of webviews in Flutter. There are 3 popular plugins

  • flutter_webview_plugin - (community)
  • Webview_flutter (official)
  • flutter_inappwebview

There is a note at README in community repository

We are working closely with the Flutter Team to integrate all the Community Plugin features in the Official WebView Plugin. We will try our best to resolve PRs and Bugfixes, but our priority right now is to merge our two code-bases. Once the merge is complete we will deprecate the Community Plugin in favor of the Official one

There is a lot of work yet to build fully working and bugfree webview. Currently for more challenging tasks like this mentioned here, the best attempt is to use flutter_inappwebview which is very popular and used by a lot people with success. There is issue associated with blob files. As we can see in your snippet you already used this plugin. To download blob file you can try convert blob:url to base64 like in this case Download Blob file from Website inside Android WebViewClient

Possible workaround

To your webview (_controller) add JavaScriptHandler. I would assume onWebViewCreated might be ok.

        controller.addJavaScriptHandler(
          handlerName: _webViewHandlerName,
          callback: (data) async {
            if (data.isNotEmpty) {
              final String receivedFileInBase64 = data[0];
              final String receivedMimeType = data[1];

              // NOTE: create a method that will handle your extensions
              final String yourExtension =
                  _mapMimeTypeToYourExtension(receivedMimeType); // 'pdf'

              _createFileFromBase64(
                  receivedFileInBase64, 'YourFileName', yourExtension);
            }
          },
        );

JavaScript handler will receive two values stored in array. First argument is file encoded in base64 and second one is mimeType e.g. application/pdf. Having information about mimeType allows us to get information about extension which should be used to save file with. They can be easly mapped application/pdf => 'pdf' etc.

  _createFileFromBase64(String base64content, String fileName, String yourExtension) async {
    var bytes = base64Decode(base64content.replaceAll('\n', ''));
    final output = await getExternalStorageDirectory();
    final file = File("${output.path}/$fileName.$yourExtension");
    await file.writeAsBytes(bytes.buffer.asUint8List());
    print("${output.path}/${fileName}.$yourExtension");
    await OpenFile.open("${output.path}/$fileName.$yourExtension");
    setState(() {});
  }

Finally where blob url can be handled the JavaScript is invoked.

       onDownloadStart: (controller, url) async {
        print("onDownloadStart $url");
        var jsContent = await rootBundle.loadString("assets/js/base64.js");
        await controller.evaluateJavascript(
            source: jsContent.replaceAll("blobUrlPlaceholder", url));
      },

Javascript (I prefer to load it as an asset base64.js, better than hardcoded in dart code) It opens blob url and pass to encodedBase64 data and mimeType as arguments to our handler blobToBase64Handler in dart.

var xhr = new XMLHttpRequest();
var blobUrl = "blobUrlPlaceholder";
console.log(blobUrl);
xhr.open('GET', blobUrl, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
  if (this.status == 200) {
    var blob = this.response;
    var reader = new FileReader();
    reader.readAsDataURL(blob);
    reader.onloadend = function() {
      var base64data = reader.result;
      var base64ContentArray = base64data.split(",")     ;
      var mimeType = base64ContentArray[0].match(/[^:\s*]\w+\/[\w-+\d.]+(?=[;| ])/)[0];
      var decodedFile = base64ContentArray[1];
      console.log(mimeType);
      window.flutter_inappwebview.callHandler('blobToBase64Handler', decodedFile, mimeType);
    };
  };
};
xhr.send();

source: Download Blob file from Website inside Android WebViewClient

source: How to decode base64 PDF string in Flutter?

It's not clean and looks hacky but could not find better and easier

like image 125
wpazio Avatar answered Oct 25 '22 10:10

wpazio


I wrote a solution for both Android and iOS. which may support any type of files.

  1. Build a simple backend using Fluter with Jaguar or other backend framework
  2. Convert the blob as formData and post it to Flutter.

The full code: https://github.com/guoguoguilai/flutter-webview-blob-download

like image 25
PageGuo Avatar answered Oct 25 '22 10:10

PageGuo