Flutter从Google Drive下载APK并安装失败求助
解决方案:Flutter APK下载安装失败问题修复
针对你遇到的APK未下载完成、找不到文件的问题,从Google Drive链接处理、权限适配、存储路径、安装流程四个核心点修复,具体步骤如下:
1. 修复Google Drive大文件下载问题
20MB的APK属于Google Drive大文件范畴,直接用uc?export=download链接会触发验证页面,导致Dio无法正确下载。通过Dio拦截器处理跳转和Cookie:
// 在下载方法中添加拦截器 final dio = Dio(); dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { // 替换为你的Drive文件ID options.headers['Cookie'] = 'download_warning_1qh2axZcDbj_OZn_4NHI5CUWTR0ocNFiN=1'; handler.next(options); }, onResponse: (response, handler) { // 处理Google Drive的确认跳转 if (response.headers['location'] != null) { final redirectUrl = response.headers['location']![0]; if (redirectUrl.contains('confirm')) { dio.download(redirectUrl, savePath, onReceiveProgress: (count, total) { print('下载进度:${(count/total*100).toStringAsFixed(2)}%'); }); return handler.resolve(response); } } handler.next(response); }, ));
2. 适配Android 10+存储权限
Android 10(API 29)及以上废弃了WRITE_EXTERNAL_STORAGE权限,应用私有存储无需申请权限,同时Android 11+需要单独申请安装权限:
修改权限请求方法
Future<bool> _requestPermission() async { if (!Platform.isAndroid) return true; final androidInfo = await DeviceInfoPlugin().androidInfo; final sdkVersion = androidInfo.version.sdkInt; if (sdkVersion >= 30) { // Android 11+ 申请安装权限 var installStatus = await Permission.requestInstallPackages.status; if (installStatus.isDenied) { installStatus = await Permission.requestInstallPackages.request(); } return installStatus.isGranted; } else { // Android 9及以下 申请存储权限 var storageStatus = await Permission.storage.status; if (storageStatus.isDenied) { storageStatus = await Permission.storage.request(); } return storageStatus.isGranted; } }
更新AndroidManifest.xml
移除冗余权限,添加Android 11+包可见性配置:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> <!-- Android 11+ 允许调用安装器 --> <queries> <intent> <action android:name="android.intent.action.VIEW" /> <data android:scheme="content" /> <data android:scheme="file" /> <data android:mimeType="application/vnd.android.package-archive" /> </intent> </queries>
3. 更换可访问的存储路径
getExternalStorageDirectory()在Android 11+访问受限,改用应用外部私有目录,无需权限且安装器可访问:
final directory = await getExternalFilesDir(null); final savePath = '${directory!.path}/replace.apk';
4. 增加下载验证与排查日志
添加进度监听、文件存在性检查,方便定位问题:
// 下载前删除旧文件 final file = File(savePath); if (await file.exists()) { await file.delete(); } // 监听下载进度 await dio.download(apkUrl, savePath, onReceiveProgress: (count, total) { if (total != -1) { print('已下载:${count/1024/1024}MB / 总大小:${total/1024/1024}MB'); } }); // 验证文件是否下载完成 if (await file.exists()) { final fileSize = await file.length(); print('文件保存成功,路径:$savePath,大小:${fileSize/1024/1024}MB'); await OpenFile.open(savePath); } else { print('错误:下载完成后未找到文件'); }
最终完整代码
需要先在pubspec.yaml添加依赖:
dependencies: flutter: sdk: flutter dio: ^5.4.0+1 permission_handler: ^11.0.1 open_file: ^3.3.2 device_info_plus: ^9.1.1
完整main.dart代码:
import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:open_file/open_file.dart'; import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { final String apkUrl = 'https://drive.google.com/uc?export=download&id=1qh2axZcDbj_OZn_4NHI5CUWTR0ocNFiN'; final String fileId = '1qh2axZcDbj_OZn_4NHI5CUWTR0ocNFiN'; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('DCS App'), ), body: Center( child: ElevatedButton( onPressed: downloadAndInstallApk, child: const Text('下载并安装DCS App'), ), ), ); } Future<void> downloadAndInstallApk() async { if (await _requestPermission()) { final directory = await getExternalFilesDir(null); final savePath = '${directory!.path}/replace.apk'; final file = File(savePath); if (await file.exists()) { await file.delete(); print('已删除旧APK文件'); } try { final dio = Dio(); dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { options.headers['Cookie'] = 'download_warning_$fileId=1'; handler.next(options); }, onResponse: (response, handler) { if (response.headers['location'] != null) { final redirectUrl = response.headers['location']![0]; if (redirectUrl.contains('confirm')) { dio.download(redirectUrl, savePath, onReceiveProgress: (count, total) { _printDownloadProgress(count, total); }); return handler.resolve(response); } } handler.next(response); }, )); await dio.download(apkUrl, savePath, onReceiveProgress: (count, total) { _printDownloadProgress(count, total); }); if (await file.exists()) { final fileSize = await file.length(); print('文件保存成功,路径:$savePath,大小:${fileSize/1024/1024}MB'); final result = await OpenFile.open(savePath); print('安装启动结果:${result.message}'); } else { print('错误:下载完成后未找到文件'); } } catch (e, stackTrace) { print('下载失败:$e'); print('堆栈信息:$stackTrace'); } } else { print('权限被拒绝,无法继续'); } } void _printDownloadProgress(int count, int total) { if (total != -1) { final progress = (count / total * 100).toStringAsFixed(2); print('下载进度:$progress%,已下载:${count/1024/1024}MB,总大小:${total/1024/1024}MB'); } else { print('已下载:${count/1024/1024}MB'); } } Future<bool> _requestPermission() async { if (!Platform.isAndroid) return true; final androidInfo = await DeviceInfoPlugin().androidInfo; final sdkVersion = androidInfo.version.sdkInt; if (sdkVersion >= 30) { var installStatus = await Permission.requestInstallPackages.status; if (installStatus.isDenied) { installStatus = await Permission.requestInstallPackages.request(); } return installStatus.isGranted; } else { var storageStatus = await Permission.storage.status; if (storageStatus.isDenied) { storageStatus = await Permission.storage.request(); } return storageStatus.isGranted; } } }
内容的提问来源于stack exchange,提问作者PLAA Hemantha
相关产品推荐
相关产品推荐

