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

Unity开发Meta Quest 2双APP文件读写权限异常求助

Meta Quest 2跨应用文件读取权限问题

我正在为Meta Quest 2开发两款应用:ADMIN和CLIENT。ADMIN应用可配置并保存数据,CLIENT应用需要加载这些数据,但当CLIENT尝试读取文件时,抛出如下错误:

UnauthorizedAccessException Access to path storage/emulated/0/Documents is denied

尽管已正确配置所有权限,问题依然存在。我的保存与加载代码如下:

using System;
using System.IO;
using System.Text;
using UnityEngine;
using UnityEngine.Android;
using System.Security.AccessControl;
using Microsoft.Win32.SafeHandles;


/// <summary>
/// Save System.
/// Use these APIs to Save/Load game data.
/// </summary>
public static class SaveSystem
{

    private static string selectedFilePath;
    private static ISaveSystemSerializer m_Serializer = new SaveSystemJsonSerializer();
    private static Encoding m_Encoding = Encoding.UTF8;

    /// <summary>
    /// Gets or sets the serializer.
    /// </summary>
    /// <value>The serializer.</value>
    public static ISaveSystemSerializer Serializer
    {
        get => m_Serializer ??= new SaveSystemJsonSerializer();
        set => m_Serializer = value;
    }

        

    /// <summary>
    /// Gets or sets the encoding.
    /// </summary>
    /// <value>The encoding.</value>
    public static Encoding DefaultEncoding
    {
        get => m_Encoding ??= Encoding.UTF8;
        set => m_Encoding = value;
    }

    /// <summary>
    /// Gets or sets a value indicating whether this <see cref="FG_Saving.SaveSystem"/> encrypt data by default.
    /// </summary>
    /// <value><c>true</c> if encode; otherwise, <c>false</c>.</value>
    public static bool Encode { get; set; } = false;

    public static string Path { get; set; } = "storage/emulated/0/Documents";


    /// <summary>
    /// Gets or sets the encryption password.
    /// </summary>
    /// <value>The encryption password.</value>
    public static string EncodePassword { get; set; } = "dabkaksdksadkj";

    /// <summary>
    /// Gets or sets a value indicating whether this <see cref="FG_Saving.SaveSystem"/> log error.
    /// </summary>
    /// <value><c>true</c> if log error; otherwise, <c>false</c>.</value>
    public static bool LogError { get; set; } = false;

    /// <summary>
    /// Saves data using the identifier.
    /// </summary>
    /// <param name="identifier">Identifier.</param>
    /// <param name="obj">Object to save.</param>
    /// <param name="saveFile"> Save File Number</param>
    /// <typeparam name="T">The 1st type parameter.</typeparam>
    public static void Save<T>(string identifier, T obj, int saveFile)
    {
        Save<T>(identifier, obj, saveFile, Encode, EncodePassword, Serializer, DefaultEncoding);
    }

    /// <summary>
    /// Saves data using the identifier.
    /// </summary>
    /// <param name="identifier">Identifier.</param>
    /// <param name="data">data to save.</param>
    /// <param name="saveFile"> Save File Number</param>
    /// <param name="encode">Encrypt the data?</param>
    /// <param name="password">Encryption Password.</param>
    /// <param name="serializer">Serializer.</param>
    /// <param name="encoding">Encoding.</param>
    /// <typeparam name="T">The 1st type parameter.</typeparam>
    public static void Save<T>(string identifier, T data, int saveFile, bool encode, string password, ISaveSystemSerializer serializer, Encoding encoding)
    {
        if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite) ||
            Permission.HasUserAuthorizedPermission(Permission.ExternalStorageRead))
        {
            Permission.RequestUserPermissions(new []{Permission.ExternalStorageRead, Permission.ExternalStorageWrite } );
            Debug.Log("Permission Granted"); 
        }
        
        if (string.IsNullOrEmpty(identifier)) throw new System.ArgumentNullException(nameof(identifier));
        
        serializer ??= Serializer;
        encoding ??= DefaultEncoding;
        string filePath = "";
            
        
        if (identifier == "Settings")
        {
            filePath = $"{Path}/{identifier + ".cfg"}";
        }
        else
        {
            filePath = $"{Path}/{identifier}{saveFile:D3}.vrm";
        }

        
            
        data ??= default(T);
        Stream stream = null;

        
        if (encode)
        {
            stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
        }
        else
        {
            stream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
        }
        
        serializer.Serialize(data, stream, encoding);
        
        stream.Dispose();
        Debug.Log("saved" + filePath);
            
    }

    /// <summary>
    /// Loads data using identifier.
    /// </summary>
    /// <param name="identifier">Identifier.</param>
    /// <param name="saveSlot"> The save slot to load from</param>
    /// <typeparam name="T">The 1st type parameter.</typeparam>
    public static T Load<T>(string identifier, int saveSlot)
    {
        return Load<T>(identifier, saveSlot, default(T), Encode, EncodePassword, Serializer, DefaultEncoding);
    }

    /// <summary>
    /// Loads data using identifier.
    /// </summary>
    /// <param name="identifier">Identifier.</param>
    /// <param name="saveSlot"> the save slot to load from</param>
    /// <param name="defaultValue">Default Value.</param>
    /// <param name="encode">Load encrypted data? (set it to true if you have used encryption in save)</param>
    /// <param name="password">Encryption Password.</param>
    /// <param name="serializer">Serializer.</param>
    /// <param name="encoder">Encoder.</param>
    /// <param name="encoding">Encoding.</param>
    /// <param name="path">Path.</param>
    /// <typeparam name="T">The 1st type parameter.</typeparam>
    public static T Load<T>(string identifier, int saveSlot, T defaultValue, bool encode, string password, ISaveSystemSerializer serializer, Encoding encoding)
    {
            
        if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageRead))
        {
            Permission.RequestUserPermissions(new []{Permission.ExternalStorageRead, Permission.ExternalStorageWrite } );
            Debug.Log("Permission Granted");
        }
        if (string.IsNullOrEmpty(identifier))
            throw new ArgumentNullException(nameof(identifier));
            
        serializer ??= Serializer;
        encoding ??= DefaultEncoding;
        defaultValue ??= default(T);
            
        T result = defaultValue;
        string filePath = "";
            
        if (identifier == "Settings") 
            filePath = $"{Path}/{identifier + ".cfg"}";
        else
            filePath = $"{Path}/{identifier}{saveSlot:D3}.vrm";
            
        if (Exists(identifier, saveSlot))
        {
            Stream stream = null;
            stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            result = serializer.Deserialize<T>(stream, encoding);
            stream.Dispose();
            Debug.Log("loaded" + filePath);
            return result;
        }
        return default;
    }

        
    /// <summary>
    /// Checks whether the specified identifier exists or not.
    /// </summary>
    /// <param name="identifier">Identifier.</param>
    /// <param name="saveSlot"> The save slot to load from</param>
    public static bool Exists(string identifier, int saveSlot) => 
        File.Exists($"{Path}/{identifier}{saveSlot:D3}.vrm");
            

    /// <summary>
    /// Delete the specified identifier and path.
    /// </summary>
    /// <param name="identifier">Identifier.</param>
    /// <param name="saveSlot"> the save slot to load from </param>
    /// <param name="path">Path.</param>
    public static void Delete(string identifier, int saveSlot)
    {
        if (string.IsNullOrEmpty(identifier))
            throw new System.ArgumentNullException(nameof(identifier));
            
        string filePath = "";
        if (identifier == "Settings") 
            filePath = $"{Path}/{identifier + ".cfg"}";
        else
            filePath = $"{Path}/{identifier}{saveSlot:D3}.vrm";
            
        if (!Exists(filePath, saveSlot)) return;
        File.Delete(filePath);
    }
   
}

我尝试过使用Unity的AndroidJavaClass加载文件,但因不熟悉Java,最终未成功解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:50:55