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

Xamarin Forms无token.json时如何编程获取Google Drive API凭证

问题根因

FileDataStore是Google .NET客户端库为桌面/控制台场景设计的默认凭证存储实现,未做任何移动端适配,在Xamarin.Android环境下运行存在两个不可绕过的兼容问题:

  • 路径处理逻辑存在bug:传入完整的token.json文件路径后,其内部逻辑会将该路径识别为文件夹,额外拼接一层和用户、授权范围相关的子路径与文件名,直接导致路径错乱,这也是测试中出现文件找不到、文件已存在类错误的核心原因
  • 文件IO逻辑不匹配安卓沙箱规则:默认实现的文件创建、读写权限逻辑未适配安卓应用私有目录的访问规范,会触发只读、权限拒绝类错误
    另外原代码中使用.Result阻塞异步方法,在移动端UI线程调用时极易引发死锁,属于不推荐的写法。
解决方案

弃用默认的FileDataStore,自行实现IDataStore接口对接Xamarin.Forms跨平台合法存储路径,绕开默认实现的兼容问题。

1. 实现移动端适配的自定义IDataStore

using Google.Apis.Json;
using Google.Apis.Util.Store;
using System.IO;
using System.Threading.Tasks;
using Xamarin.Essentials;

public class MobileFileDataStore : IDataStore
{
    private readonly string _appDataDir;

    public MobileFileDataStore()
    {
        // 自动映射到各平台应用私有目录,安卓API24及以上无需额外申请存储权限即可读写
        _appDataDir = FileSystem.AppDataDirectory;
        if (!Directory.Exists(_appDataDir))
        {
            Directory.CreateDirectory(_appDataDir);
        }
    }

    public Task ClearAsync()
    {
        if (Directory.Exists(_appDataDir))
        {
            Directory.Delete(_appDataDir, true);
            Directory.CreateDirectory(_appDataDir);
        }
        return Task.CompletedTask;
    }

    public Task DeleteAsync<T>(string key)
    {
        var targetFile = Path.Combine(_appDataDir, $"{key}.json");
        if (File.Exists(targetFile)) File.Delete(targetFile);
        return Task.CompletedTask;
    }

    public Task<T> GetAsync<T>(string key)
    {
        var targetFile = Path.Combine(_appDataDir, $"{key}.json");
        if (!File.Exists(targetFile)) return Task.FromResult(default(T));
        
        using var stream = File.OpenRead(targetFile);
        return Task.FromResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(stream));
    }

    public Task StoreAsync<T>(string key, T value)
    {
        var targetFile = Path.Combine(_appDataDir, $"{key}.json");
        if (File.Exists(targetFile)) File.Delete(targetFile);
        
        using var stream = File.Create(targetFile);
        NewtonsoftJsonSerializer.Instance.Serialize(stream, value);
        return Task.CompletedTask;
    }
}

若项目未引用Xamarin.Essentials,可手动替换FileSystem.AppDataDirectory为Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),效果一致。

2. 修改授权调用逻辑

将原同步阻塞写法改为异步调用,替换默认FileDataStore为自定义实现:

// 方法标记为async,避免阻塞
public static async Task DriveCreateFolder(string credjson)
{
    try
    {
        byte[] byteArray = Encoding.ASCII.GetBytes(credjson);
        UserCredential credential;

        using (var stream = new MemoryStream(byteArray))
        {
            // 替换为自定义存储实现
            credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                 GoogleClientSecrets.FromStream(stream).Secrets,
                Scopes,
                "user",
                CancellationToken.None, 
                new MobileFileDataStore());
        }

        // 以下Drive服务初始化、文件夹创建逻辑保持原有逻辑即可
        var service = new DriveService(new BaseClientService.Initializer
        {
            HttpClientInitializer = credential,
            ApplicationName = ApplicationName
        });

        var fileMetadata = new Google.Apis.Drive.v3.Data.File()
        {
            Name = "MyAppFolder",
            MimeType = "application/vnd.google-apps.folder"
        };

        var request = service.Files.Create(fileMetadata);
        request.Fields = "id";
        var file = request.Execute();
        Console.WriteLine("Folder ID: " + file.Id);
        ApplicationFolder = file; 
    }
    catch (AggregateException)
    {
        Console.WriteLine("Credential load failed");
    }
    catch (Exception e)
    {
        Console.WriteLine($"Operation error: {e.Message}");
        throw;
    }
}

3. 安卓端必要配置

在Android项目的AndroidManifest.xml中添加网络权限,否则授权流程无法访问Google服务:

<uses-permission android:name="android.permission.INTERNET" />

同时确认Google Cloud控制台中OAuth客户端的配置:选择「已安装的应用」类型,填写的安卓包名、签名证书SHA1值与测试包完全匹配,否则会出现授权被拒绝的问题。

测试报错原因对应说明
  • 测试1:传入相对路径token.json时,安卓默认工作目录为只读系统分区,触发只读文件系统错误
  • 测试2/3:默认FileDataStore将传入的token.json文件路径识别为文件夹,尝试在该路径下创建子文件时,因父路径实际是文件而非目录,触发文件找不到、访问拒绝错误
  • 测试4:默认FileDataStore写文件时未做存在性兼容处理,发现目标路径已有同名文件直接抛出异常,属于实现逻辑bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 10:42:51