Xamarin下载PDF时遭遇未授权访问权限问题求助
问题分析与解决方案
一、原权限报错的根本原因
- 路径参数错误:原代码中
File.Create(path)传入的是目录路径(Environment.SpecialFolder.MyDocuments指向目录),但File.Create需要具体的文件路径(必须包含文件名,比如xxx.pdf),直接传入目录会触发权限或操作错误。 - Android存储机制限制:Android 10(API 29)引入Scoped Storage,即使申请了
StorageRead/StorageWrite权限,直接用File API操作公共目录会被拒绝;而应用私有目录本身自带读写权限,原权限申请属于冗余操作。
二、下载后找不到文件的解决方案
FileSystem.AppDataDirectory是应用私有目录,默认仅应用自身可访问,普通文件管理器无法查看。以下是几种可行方案:
方案1:保存到公共下载目录(推荐)
将文件保存到系统公共下载目录,用户可直接在“下载”文件夹中找到。
代码修改:
首先修复权限检查的逻辑错误:
public async Task<bool> CheckStorage() { var readPermission = await Permissions.CheckStatusAsync<Permissions.StorageRead>(); var writePermission = await Permissions.CheckStatusAsync<Permissions.StorageWrite>(); if (readPermission != PermissionStatus.Granted || writePermission != PermissionStatus.Granted) { readPermission = await Permissions.RequestAsync<Permissions.StorageRead>(); writePermission = await Permissions.RequestAsync<Permissions.StorageWrite>(); } // 需同时拥有读写权限才返回true return readPermission == PermissionStatus.Granted && writePermission == PermissionStatus.Granted; }
然后修改下载函数,适配Android版本处理公共目录写入:
public async Task<bool> DownloadFile(string fileURL) { string fileName = $"AdoptPaw_{DateTime.Now:yyyyMMddHHmmss}.pdf"; try { using (var client = new HttpClient()) { using (var downloadStream = await client.GetStreamAsync(fileURL)) { if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Q) { // Android 10+ 使用MediaStore写入公共下载目录 var values = new ContentValues(); values.Put(MediaStore.MediaColumns.DisplayName, fileName); values.Put(MediaStore.MediaColumns.MimeType, "application/pdf"); values.Put(MediaStore.MediaColumns.RelativePath, Android.OS.Environment.DirectoryDownloads); var uri = Android.App.Application.Context.ContentResolver.Insert(MediaStore.Downloads.ExternalContentUri, values); using (var fileStream = Android.App.Application.Context.ContentResolver.OpenOutputStream(uri)) { await downloadStream.CopyToAsync(fileStream); } } else { // 低于Android 10,直接写入公共下载目录 string publicDownloadPath = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, Android.OS.Environment.DirectoryDownloads); Directory.CreateDirectory(publicDownloadPath); string filePath = Path.Combine(publicDownloadPath, fileName); using (var fileStream = File.Create(filePath)) { await downloadStream.CopyToAsync(fileStream); } } } } return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"下载失败:{ex.Message}"); return false; } }
方案2:从私有目录分享文件
若不想保存到公共目录,可在下载完成后通过分享功能让用户选择应用打开文件:
// 下载完成后调用此方法 public async Task ShareDownloadedFile() { string filePath = Path.Combine(FileSystem.AppDataDirectory, "File.pdf"); if (File.Exists(filePath)) { await Share.RequestAsync(new ShareFileRequest { Title = "查看领养PDF", File = new ShareFile(filePath) }); } }
方案3:调试时查看私有目录
若仅需确认文件是否下载成功,可通过ADB命令查看:
adb shell run-as com.companyname.pawadopt_v5 ls files
或在Android Studio的Device File Explorer中找到data/data/com.companyname.pawadopt_v5/files目录。
三、关键注意事项
- Android 11+(API 30)开始,
requestLegacyExternalStorage属性失效,必须使用MediaStore或SAF访问公共目录,不能依赖旧的File API。 - 若仅保存到应用私有目录,可直接移除
StorageRead/StorageWrite权限申请代码,私有目录默认拥有读写权限。 - 建议给文件名添加时间戳,避免覆盖已存在的文件。
内容的提问来源于stack exchange,提问作者Lomon13
相关产品推荐
相关产品推荐

