我正在开发一个 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 并可以提供一些指导吗? 任何见解或建议将不胜感激。预先感谢您的帮助!
如果可以的话,我认为你应该尝试在移动端而不是网络上处理下载。
我在尝试
screenshot
webview 内容和 send
将其作为 base64 传输到移动设备时遇到了类似的问题。
Web 视图没有响应,因为 Web 视图中用于捕获内容的库非常慢。如果我只有
send
base64 数据,一切都会正常工作。
您可以通过
chrome://inspect
(Android 设备)和 safari develop
(iOS 设备)调试 webview 以检查下载功能
希望这有帮助!