• flutter 的 in_app_web_view实现下载功能


    flutter与前端交互,利用in_app_web_view实现下载功能:

    首先下载库,终端输入

    flutter pub add flutter_inappwebview

    之后导出

    import 'package:flutter_inappwebview/flutter_inappwebview.dart';

    即可使用。

    创建in_app_web_view:

    复制代码
    InAppWebView(
         initialOptions:
              InAppWebViewGroupOptions(
                  crossPlatform:InAppWebViewOptions(
                    useOnDownloadStart:true,
                  ),
                  android: AndroidInAppWebViewOptions()
              ),
          //老版本:initialUrl    新版本:initialUrlRequest
          initialUrlRequest: URLRequest(
            url: Uri.parse(widget.url),
          )
    )
    复制代码

    因为要下载文件,所以请务必手动设置 useOnDownloadStart 为 true(否则出发文件下载的监听)。

    initialUrlRequest中可填写自己想首先打开的url地址。

    可参考例子:flutter_inappwebview_examples/main.dart at main · pichillilorenzo/flutter_inappwebview_examples · GitHub

    https://github.com/pichillilorenzo/flutter_inappwebview_examples/blob/main/file_download/lib/main.dart

    填写自己需要的回调(例子中的一点错误,没有开启 useOnDownloadStart, 因此不会下载成功,在使用时请设置为true)

    正常情况下,配合downloader和android_path_provider,普通https链接即可下载文件。

     

    而遇到blob链接时,还需要进行更多操作来确保文件的下载:

    可参考javascript - Flutter WebView blob pdf download - Stack Overflow

    https://stackoverflow.com/questions/64865972/flutter-webview-blob-pdf-download/64902313#64902313

    因为Android不支持blob链接下载,因此我们嵌套javascript处理下载链接,在in_app_web_view的build中重写onWebViewCreated方法,添加javascriptHandler:

    复制代码
    onWebViewCreated: (InAppWebViewController controller) {
            if (mounted) {
              setState(() {
                _inAppWebCtrl = controller;
                _inAppWebCtrl!.addJavaScriptHandler(
                  handlerName: 'blobToBase64Handler',
                  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 extension =
                      _mapMimeTypeToExtension(receivedMimeType);
                      String tmpFileName = 'tmpfile';
                      _createFileFromBase64(
                          receivedFileInBase64, tmpFileName, extension);
                    }
                  },
                );
              });
            }
          },
    复制代码

    首先在assets中添加js文件夹,然后创建 base64.js 文件

    复制代码
    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();
    复制代码

     

    注意js中的callhander的名字参数,对应创建webview时addJavascriptHandler中的name。

    另外是文件类型映射函数和文件下载函数:

    复制代码
      String _mapMimeTypeToExtension(String mimeType) {
        String extension = '';
        switch(mimeType) {
          case 'image/png': extension = 'png'; break;
          case 'application/msword': extension = 'doc'; break;
          case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
            extension = 'docx';
            break;
          case 'image/jpeg': extension = 'jpg'; break;
          case 'image/gif': extension = 'gif'; break;
          case 'image/svg+xml': extension = 'svg'; break;
          case 'image/tiff': extension = 'tif'; break;
          case 'text/plain': extension = 'txt'; break;
          case 'application/vnd.ms-powerpoint': extension = 'ppt'; break;
          case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
            extension = 'pptx';
            break;
          case 'application/vnd.ms-excel': extension = 'xls'; break;
          case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
            extension = 'xlsx';
            break;
          case 'application/zip': extension = 'zip'; break;
          case 'application/x-7z-compressed': extension = '7z'; break;
          case 'application/pdf': extension = 'pdf'; break;
        }
        return extension;
      }
    
      _createFileFromBase64(String base64content, String fileName, String yourExtension) async {
        var bytes = base64Decode(base64content.replaceAll('\n', ''));
        final file = File("$_localPath/$fileName.$yourExtension");
        await file.writeAsBytes(bytes.buffer.asUint8List());
        
      }
    复制代码

    最后重写inappwebview中的下载请求方法:

    复制代码
          onDownloadStartRequest: (controller, downloadStartRequest) async {
              var jsContent = await rootBundle.loadString("assets/js/base64.js");
    // 运行javascript代码解析blob
              await controller.evaluateJavascript(
                  source: jsContent.replaceAll("blobUrlPlaceholder",
                      downloadStartRequest.url.toString()));
          },
    复制代码

    总结:因为android本身不能解析blob,我们因此使用javascript作为翻译:运行顺序:

    onDownloadStartRequest -> javascript文件 -> webviewController中的handler的callback,最后以流的方式写入文件。

     

  • 相关阅读:
    ASPICE实操中的那点事儿-底层软件的单元测试该如何做
    富格林:可信技巧隔绝遭遇欺诈
    DevOps(十二)Jenkins实战之Web发布到远程服务器
    vue部分手写面试题
    【刷题笔记8】LeetCode 48. 旋转图像(数组模拟)
    为什么 Vue3.js / Element+ 组件属性前面有的需要添加冒号,有的不需要?
    R语言 | 数据框
    netty websockt之断连重试
    如何利用PHP快速抓取音频数据?
    Web基础与HTTP协议
  • 原文地址:https://www.cnblogs.com/jiang448/p/17463750.html