Flutter WebView blob pdf 下载

问题描述 投票:0回答:3

我正在尝试使用flutter从网站下载文件,我首先使用evaluateJavaScript并更改了一些值,然后单击生成按钮,该按钮应该将适当的pdf文件下载到手机。

这是我的代码:

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,);
          },

      ),

打印出来的网址是这样的

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

这是控制台:

有人可以帮助我吗?

javascript flutter android-webview blob
3个回答
16
投票

使用 blob url 下载文件很棘手,并且在 Flutter 中 webview 的当前状态下不支持开箱即用。有3个流行插件

社区存储库中的自述文件中有一条注释

我们正在与 Flutter 团队密切合作,整合所有 官方 WebView 插件中的社区插件功能。我们会尝试 我们尽力解决 PR 和 Bug 修复,但我们现在的首要任务是 合并我们的两个代码库。合并完成后我们将弃用 社区插件有利于官方插件

要构建完全可用且无错误的 Web 视图,还有很多工作要做。目前,对于像这里提到的更具挑战性的任务,最好的尝试是使用flutter_inappwebview,它非常流行并且被很多人成功使用。 存在与 blob 文件相关的问题。正如我们在您的代码片段中看到的,您已经使用了这个插件。要下载 Blob 文件,您可以尝试将 blob:url 转换为 base64,就像在本例中一样 从 Android WebViewClient 内的网站下载 Blob 文件

可能的解决方法

在您的网络视图 (

_controller
) 中添加
JavaScriptHandler
。我认为
onWebViewCreated
可能没问题。

        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 处理程序将接收存储在数组中的两个值。第一个参数是以 base64 编码的文件,第二个参数是 mimeType,例如

application/pdf
。拥有有关 mimeType 的信息使我们能够获取有关用于保存文件的扩展名的信息。 它们可以轻松映射 application/pdf => 'pdf' 等。

  _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(() {});
  }

最后,在可以处理 blob url 的地方,将调用 JavaScript。

       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(我更喜欢将其作为资产 base64.js 加载,比在 dart 代码中硬编码更好) 它打开 blob url 并传递给encodedBase64 数据和 mimeType 作为参数传递给 dart 中的处理程序

blobToBase64Handler

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();

来源:从 Android WebViewClient 内的网站下载 Blob 文件

来源:如何在 Flutter 中解码 Base64 PDF 字符串?

它不干净,看起来很破旧,但找不到更好和更容易的


3
投票

我为 Android 和 iOS 编写了一个解决方案。它可以支持任何类型的文件。

  1. 使用 Fluter 与 Jaguar 或其他后端框架构建一个简单的后端
  2. 将 blob 转换为 formData 并将其发布到 Flutter。

完整代码:https://github.com/guoguoguilai/flutter-webview-blob-download


0
投票

以防万一有人会发现这个问题,我的 blob 下载解决方案:

    controller = WebViewController()
      ..addJavaScriptChannel('Mobile', onMessageReceived: (JavaScriptMessage message) {
          Map<String, dynamic> data = jsonDecode(message.message);
          if (data['action'] == 'fileDownload') {
            saveToFile(data['data']);
          }
        })
      ..setNavigationDelegate(
        NavigationDelegate(
          onPageFinished: (String url) {
            controller.runJavaScript("""
if (!window.mobilelistener) {
  window.mobilelistener = true;
  document.addEventListener('click', async function(event) {
    var element = event.target; 
    if (element.tagName === 'A' && element.href.startsWith("blob")) {
      event.preventDefault();
      var reader = new FileReader();
      reader.onloadend = function() {
        var base64data = reader.result;
        Mobile.postMessage(JSON.stringify(
          {
            "action": "fileDownload",
            "data": base64data
          }
        ));
      }
      fetch(element.href)
        .then(r => r.blob())
        .then(blob => reader.readAsDataURL(blob));
    }
  });
}
            """);
          },
          onNavigationRequest: (NavigationRequest request) {
            if (request.url.startsWith('blob:')) {
              return NavigationDecision.prevent;
            }

保存到文件是这样的:

void saveToFile(String content) async {
    content = content.substring(content.indexOf('base64,') + 7);
    Uint8List bytes = base64Decode(content);

    String? path = await FileSaver.instance.saveFile(
      name: 'file',
      bytes: bytes,
      ext: 'pdf',
      mimeType: MimeType.pdf);
}
© www.soinside.com 2019 - 2024. All rights reserved.