You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Hybrid安卓应用:授权外部应用编辑自建文件的问题

解决Blazor Hybrid安卓应用文件操作问题

问题背景

使用Visual Studio 2022+C#开发Blazor Hybrid安卓应用,需实现以下功能:

  • 创建并存储含基础内容的文本文件(已实现)
  • 通过Launcher.Default.OpenAsync启动应用或Share.RequestAsync分享文件,允许用户选择应用修改该文本文件
  • 允许用户将修改后的文件保存回应用初始存放位置

遇到的问题:

  • 步骤2:Launcher能正常打开文件,但Share功能打开空文件;改用原生Intent唤起应用选择后,打开的仍为空文件
  • 步骤3:修改后保存回原位置时触发写入权限被拒提示,尝试配置FileProvider编译失败,设置UriPermission未解决

尝试的代码片段:

var fn =  "nieuw.txt";
var docsDirectory = Android.App.Application.Context.GetExternalFilesDir(null);
var file = Path.Combine($"{docsDirectory.AbsoluteFile.Path}", fn);
var myUri = Android.Net.Uri.Parse(file);
fileExists =  File.Exists(file).ToString();
Android.Content.Intent viewDoc = new Android.Content.Intent(Android.Content.Intent.ActionView);
viewDoc.SetDataAndType(myUri,"text/plain");
viewDoc.SetFlags(Android.Content.ActivityFlags.NewTask);
viewDoc.AddFlags(Android.Content.ActivityFlags.GrantReadUriPermission);
viewDoc.AddFlags(Android.Content.ActivityFlags.GrantWriteUriPermission);
Android.App.Application.Context.StartActivity(viewDoc);
message = myUri.ToString();
string popoverTitle = "Read text file";
var fn =  "nieuw.txt";
var docsDirectory = Android.App.Application.Context.GetExternalFilesDir(null);
var file = Path.Combine($"{docsDirectory.AbsoluteFile.Path}", fn);
await Launcher.Default.OpenAsync(new OpenFileRequest(popoverTitle, new ReadOnlyFile(file)));

可行解决方法

一、正确配置FileProvider(核心解决权限与空文件问题)

  1. 添加FileProvider到AndroidManifest.xml
    在<application>标签内插入以下配置:

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
    
  2. 创建文件路径配置文件
    在Android项目的Resources目录下新建xml文件夹,创建file_paths.xml文件,内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-files-path name="external_files" path="." />
    </paths>
    

    该配置将应用的外部文件目录暴露给其他合法应用,避免直接使用文件路径导致的权限拦截。

  3. 修改Intent代码生成合法Content URI
    替换原Uri生成逻辑,改用FileProvider生成Content URI:

    var fn = "nieuw.txt";
    var docsDirectory = Android.App.Application.Context.GetExternalFilesDir(null);
    var file = new Java.IO.File(docsDirectory, fn);
    // 生成符合权限要求的Content URI
    var myUri = FileProvider.GetUriForFile(Android.App.Application.Context, 
        $"{Android.App.Application.Context.PackageName}.fileprovider", file);
    
    var viewDoc = new Android.Content.Intent(Android.Content.Intent.ActionView);
    viewDoc.SetDataAndType(myUri, "text/plain");
    // 设置正确的权限Flag
    viewDoc.SetFlags(Android.Content.ActivityFlags.NewTask | 
        Android.Content.ActivityFlags.GrantReadUriPermission | 
        Android.Content.ActivityFlags.GrantWriteUriPermission);
    
    // 给所有匹配的应用授予权限
    var packageManager = Android.App.Application.Context.PackageManager;
    var resInfoList = packageManager.QueryIntentActivities(viewDoc, Android.Content.PM.PackageInfoFlags.MatchDefaultOnly);
    foreach (var resolveInfo in resInfoList)
    {
        var packageName = resolveInfo.ActivityInfo.PackageName;
        Android.App.Application.Context.GrantUriPermission(packageName, myUri, 
            Android.Content.ActivityFlags.GrantReadUriPermission | 
            Android.Content.ActivityFlags.GrantWriteUriPermission);
    }
    
    Android.App.Application.Context.StartActivity(viewDoc);
    

二、修复Launcher.Default.OpenAsync的写入权限

原代码使用ReadOnlyFile仅提供只读权限,需改用WriteableFile并确保权限配置正确:

string popoverTitle = "Edit text file";
var fn = "nieuw.txt";
var docsDirectory = Android.App.Application.Context.GetExternalFilesDir(null);
var file = Path.Combine($"{docsDirectory.AbsoluteFile.Path}", fn);
// 使用WriteableFile允许外部应用写入
await Launcher.Default.OpenAsync(new OpenFileRequest(popoverTitle, new WriteableFile(file)));

同时在AndroidManifest.xml中添加对应权限:

<!-- Android 12及以下 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Android 13+ 无需WRITE权限,改用媒体读取权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

三、修复Share.RequestAsync的空文件问题

分享时同样需使用Content URI,而非直接文件路径:

var fn = "nieuw.txt";
var docsDirectory = Android.App.Application.Context.GetExternalFilesDir(null);
var file = new Java.IO.File(docsDirectory, fn);
var shareUri = FileProvider.GetUriForFile(Android.App.Application.Context, 
    $"{Android.App.Application.Context.PackageName}.fileprovider", file);

var shareRequest = new ShareFileRequest
{
    Title = "Share text file",
    File = new ShareFile(shareUri.ToString(), "text/plain")
};

await Share.RequestAsync(shareRequest);

四、验证文件保存回原位置的权限

应用的GetExternalFilesDir目录属于应用私有外部存储,应用本身默认拥有读写权限。若仍出现权限问题,检查:

  • 文件路径是否确实在GetExternalFilesDir返回的目录内
  • 应用是否处于后台(部分安卓版本会限制后台应用的文件操作权限)

内容的提问来源于stack exchange,提问作者MvdK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 12:00:35