“Flutter InAppWebView 无法正确处理 PDF 下载的 Blob URL”

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

我正在开发一个 Flutter 项目,其中使用 flutter_inappwebview 包来显示 Web 视图。我的目标是让用户能够下载可通过 blob URL 链接的 PDF 文件。但是,我遇到了下载功能的问题,特别是在处理 blob URL 时。 这是我的代码的相关部分:

import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';

import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';


import 'package:yeniweb/core/pdf_ac.dart';

class WebViewScreen extends StatefulWidget {
  final String url;
  WebViewScreen({required this.url});

  @override
  _WebViewScreenState createState() => _WebViewScreenState();
}



class _WebViewScreenState extends State<WebViewScreen> {
  final GlobalKey<ScaffoldMessengerState> _scaffoldKey =
  GlobalKey<ScaffoldMessengerState>();
  InAppWebViewController? webViewController;
  Dio dio = Dio();
  double downloadProgress = 0.0;
  bool isLoading = true;
  bool showSnackBar = true;

  @override
  void initState() {
    super.initState();
    webViewController?.addJavaScriptHandler(
        handlerName: 'base64String',
        callback: (args) async {
          String base64String = args[0];
          final dir = await getApplicationDocumentsDirectory();
          final filePath = p.join(dir.path, 'downloaded_file.pdf'); // Change extension as needed
          decodeBase64AndWriteToFile(base64String, filePath);
          openPDF(filePath);
        }
    );


  }
  void decodeBase64AndWriteToFile(String base64String, String filePath) {
    final bytes = base64.decode(base64String.split(',').last);
    File(filePath).writeAsBytesSync(bytes);
    // Continue with your logic (e.g., opening the file)
  }




  Future<void> startDownload(String url) async {
    try {
      if (url.isEmpty || !Uri.parse(url).isAbsolute) {
        throw 'Invalid URL';
      }

      final fileName = p.basename(url);
      final dir = await getApplicationDocumentsDirectory();
      final filePath = p.join(dir.path, fileName);

      if (!url.startsWith('blob:')) {
        await dio.download(url, filePath, onReceiveProgress: (received, total) {
          setState(() {
            downloadProgress = received / total;
          });
        });
        openPDF(filePath);
      } else {
        print("Blob URL detected, handling separately...");
        // Blob URL handling logic
      }
    } catch (e) {
      print('Download error: $e');
    }
  }

  Future<void> handleBlobUrl(String blobUrl) async {
    try {
      // JavaScript içinde blob URL'yi base64 string'e dönüştüren fonksiyonu çağırın
      print('$blobUrl');
      await webViewController?.evaluateJavascript(source: """
      (function() {
        fetch('$blobUrl')
          .then(response => response.blob())
          .then(blob => {
            const reader = new FileReader();
            reader.onloadend = function() {
              // Flutter'a base64 string'ini gönderin
              window.flutter_inappwebview.callHandler('base64String', reader.result);
            };
            reader.readAsDataURL(blob);
          })
          .catch(error => console.error('Blob conversion error:', error));
      })();
    """);
    } catch (e) {
      print('Error handling blob URL: $e');
    }
  }





  void showDownloadDialog(String url) {
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (context) {
        startDownload(url);
        return AlertDialog(
          title: Text('indiriliyor...'),
          content: StreamBuilder(
            stream: Stream.periodic(Duration(milliseconds: 500)),
            builder: (context, snapshot) {
              return LinearProgressIndicator(value: downloadProgress);
            },
          ),
          actions: <Widget>[
            TextButton(

              onPressed: () {
                Navigator.of(context).pop();
              },   child: Text('Close'),
            ),
          ],
        );
      },
    );
  }

  void openPDF(String filePath) {
    Navigator.of(context).push(MaterialPageRoute(
      builder: (context) => PDFViewPage(filePath: filePath),
    ));
  }

  @override
  Widget build(BuildContext context) {
    Future.delayed(Duration.zero, () {
      _scaffoldKey.currentState?.showSnackBar(SnackBar(
        content: Text('SnackBar içeriği'),
      ));
    });
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        backgroundColor: Colors.white70,
        elevation: 0,
        title: const Text("Web View",),

      ),

        body: Stack(
        children: <Widget>[
          InAppWebView(
            onLoadStart: (controller, url) {
              setState(() {
                isLoading = true;
              });
            },
            onLoadStop: (controller, url) {
              setState(() {
                isLoading = false;
              });
            },
            initialUrlRequest: URLRequest(url: Uri.parse(widget.url)),
            initialOptions: InAppWebViewGroupOptions(
              crossPlatform: InAppWebViewOptions(
                javaScriptEnabled: true,
                useOnDownloadStart: true,
              ),
            ),
            onWebViewCreated: (controller) {
              webViewController = controller;

              // JavaScript to handle blob URLs
              webViewController?.evaluateJavascript(source: """
    document.getElementById('link').addEventListener('click', (event) => {
      event.preventDefault();
      window.flutter_inappwebview.callHandler('downloadBlob', link.href);
    });
  """);
              controller.evaluateJavascript(source: """
            function convertBlobToBase64(blobUrl, callback) {
              fetch(blobUrl)
                .then(response => response.blob())
                .then(blob => {
                  const reader = new FileReader();
                  reader.onloadend = () => callback(reader.result);
                  reader.readAsDataURL(blob);
                })
                .catch(error => console.error('Blob conversion error:', error));
            }
          """);


              // Set up the JavaScript handler for 'downloadBlob'
              webViewController?.addJavaScriptHandler(
                  handlerName: 'downloadBlob',
                  callback: (args) {
                    // args[0] is the blob URL
                    handleBlobUrl(args[0]);
                  }
              );

              // Set up the JavaScript handler for 'base64String'
              webViewController?.addJavaScriptHandler(
                  handlerName: 'base64String',
                  callback: (args) {
                    // args[0] is the base64 string
                    // handle the base64 string here
                  }
              );
            },

            onDownloadStartRequest:  (controller, request) async {
      final url = request.url.toString();
      if (url.startsWith('blob:')) {
        // Handle blob URL differently
        print("blob algılandı");
        handleBlobUrl(url);
      } else {
        // Handle other URLs with the existing method
        showDownloadDialog(url);
        print("blob algılanmadı "
            "");
      }
    },

          ),
          isLoading ? Center(child: CircularProgressIndicator()) : SizedBox.shrink(),
        ],
      ),
    );
  }
}

当我尝试下载通过 blob URL 链接的 PDF 文件时,就会出现问题。应用程序的预期行为是获取 blob URL,将其转换为 base64 字符串,然后下载文件。相反,我观察到的是[描述观察到的行为,例如,什么也没有发生,发生错误等]。

我的一些具体问题是:

是否有更好的方法来处理 Flutter 的 WebView 中文件下载的 Blob URL? InAppWebView 中是否有我可能缺少的任何特定设置或配置才能使其正常工作? 有谁在 Flutter 中成功实现了从 blob URL 下载 PDF 并可以提供一些指导吗? 任何见解或建议将不胜感激。预先感谢您的帮助!

flutter dart blob
1个回答
0
投票

如果可以的话,我认为你应该尝试在移动端而不是网络上处理下载。

我在尝试

screenshot
webview 内容和
send
将其作为 base64 传输到移动设备时遇到了类似的问题。

Web 视图没有响应,因为 Web 视图中用于捕获内容的库非常慢。如果我只有

send
base64 数据,一切都会正常工作。

您可以通过

chrome://inspect
(Android 设备)和
safari develop
(iOS 设备)调试 webview 以检查下载功能

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.