> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 實體檔案

> 讀取、寫入及上傳附加至 PlayFab 實體的任何格式檔案，並提供涵蓋登入、載入和上傳的完整 Unity C# 實體檔案迴圈。

實體檔案可讓您讀取和寫入附加至實體的任何格式檔案。下列範例示範完整的實體檔案迴圈，從登入、載入檔案，到上傳新檔案。

```csharp theme={null}
#if !DISABLE_PLAYFABENTITY_API && !DISABLE_PLAYFABCLIENT_API

using PlayFab;
using PlayFab.Internal;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

public class EntityFileExample : MonoBehaviour
{
    public string entityId; // Id representing the logged in player
    public string entityType; // entityType representing the logged in player
    private readonly Dictionary<string, string> _entityFileJson = new Dictionary<string, string>();
    private readonly Dictionary<string, string> _tempUpdates = new Dictionary<string, string>();
    public string ActiveUploadFileName;
    public string NewFileName;
    public int GlobalFileLock = 0; // Kind of cheap and simple way to handle this kind of lock

    void OnSharedFailure(PlayFabError error)
    {
        Debug.LogError(error.GenerateErrorReport());
        GlobalFileLock -= 1;
    }

    void OnGUI()
    {
        if (!PlayFabClientAPI.IsClientLoggedIn() && GUI.Button(new Rect(0, 0, 100, 30), "Login"))
            Login();
        if (PlayFabClientAPI.IsClientLoggedIn() && GUI.Button(new Rect(0, 0, 100, 30), "LogOut"))
            PlayFabClientAPI.ForgetAllCredentials();

        if (PlayFabClientAPI.IsClientLoggedIn() && GUI.Button(new Rect(100, 0, 100, 30), "(re)Load Files"))
            LoadAllFiles();

        if (PlayFabClientAPI.IsClientLoggedIn())
        {
            // Display existing files
            _tempUpdates.Clear();
            var index = 0;
            foreach (var each in _entityFileJson)
            {
                GUI.Label(new Rect(100 * index, 60, 100, 30), each.Key);
                var tempInput = _entityFileJson[each.Key];
                var tempOutput = GUI.TextField(new Rect(100 * index, 90, 100, 30), tempInput);
                if (tempInput != tempOutput)
                    _tempUpdates[each.Key] = tempOutput;
                if (GUI.Button(new Rect(100 * index, 120, 100, 30), "Save " + each.Key))
                    UploadFile(each.Key);
                index++;
            }
            // Apply any changes
            foreach (var each in _tempUpdates)
                _entityFileJson[each.Key] = each.Value;

            // Add a new file
            NewFileName = GUI.TextField(new Rect(100 * index, 60, 100, 30), NewFileName);
            if (GUI.Button(new Rect(100 * index, 90, 100, 60), "Create " + NewFileName))
                UploadFile(NewFileName);
        }
    }

    void Login()
    {
        var request = new PlayFab.ClientModels.LoginWithCustomIDRequest
        {
            CustomId = SystemInfo.deviceUniqueIdentifier,
            CreateAccount = true
        };
        PlayFabClientAPI.LoginWithCustomID(request, OnLogin, OnSharedFailure);
    }
    void OnLogin(PlayFab.ClientModels.LoginResult result)
    {
        entityId = result.EntityToken.Entity.Id;
        entityType = result.EntityToken.Entity.Type;
    }

    void LoadAllFiles()
    {
        if (GlobalFileLock != 0)
            throw new Exception("This example overly restricts file operations for safety. Careful consideration must be made when doing multiple file operations in parallel to avoid conflict.");

        GlobalFileLock += 1; // Start GetFiles
        var request = new PlayFab.DataModels.GetFilesRequest { Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType } };
        PlayFabDataAPI.GetFiles(request, OnGetFileMeta, OnSharedFailure);
    }
    void OnGetFileMeta(PlayFab.DataModels.GetFilesResponse result)
    {
        Debug.Log("Loading " + result.Metadata.Count + " files");

        _entityFileJson.Clear();
        foreach (var eachFilePair in result.Metadata)
        {
            _entityFileJson.Add(eachFilePair.Key, null);
            GetActualFile(eachFilePair.Value);
        }
        GlobalFileLock -= 1; // Finish GetFiles
    }
    void GetActualFile(PlayFab.DataModels.GetFileMetadata fileData)
    {
        GlobalFileLock += 1; // Start Each SimpleGetCall
        PlayFabHttp.SimpleGetCall(fileData.DownloadUrl,
            result => { _entityFileJson[fileData.FileName] = Encoding.UTF8.GetString(result); GlobalFileLock -= 1; }, // Finish Each SimpleGetCall
            error => { Debug.Log(error); }
        );
    }

    void UploadFile(string fileName)
    {
        if (GlobalFileLock != 0)
            throw new Exception("This example overly restricts file operations for safety. Careful consideration must be made when doing multiple file operations in parallel to avoid conflict.");

        ActiveUploadFileName = fileName;

        GlobalFileLock += 1; // Start InitiateFileUploads
        var request = new PlayFab.DataModels.InitiateFileUploadsRequest
        {
            Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
            FileNames = new List<string> { ActiveUploadFileName },
        };
        PlayFabDataAPI.InitiateFileUploads(request, OnInitFileUpload, OnInitFailed);
    }
    void OnInitFailed(PlayFabError error)
    {
        if (error.Error == PlayFabErrorCode.EntityFileOperationPending)
        {
            // This is an error you should handle when calling InitiateFileUploads, but your resolution path might vary
            GlobalFileLock += 1; // Start AbortFileUploads
            var request = new PlayFab.DataModels.AbortFileUploadsRequest
            {
                Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
                FileNames = new List<string> { ActiveUploadFileName },
            };
            PlayFabDataAPI.AbortFileUploads(request, (result) => { GlobalFileLock -= 1; UploadFile(ActiveUploadFileName); }, OnSharedFailure); GlobalFileLock -= 1; // Finish AbortFileUploads
            GlobalFileLock -= 1; // Failed InitiateFileUploads
        }
        else
            OnSharedFailure(error);
    }
    void OnInitFileUpload(PlayFab.DataModels.InitiateFileUploadsResponse response)
    {
        string payloadStr;
        if (!_entityFileJson.TryGetValue(ActiveUploadFileName, out payloadStr))
            payloadStr = "{}";
        var payload = Encoding.UTF8.GetBytes(payloadStr);

        GlobalFileLock += 1; // Start SimplePutCall
        PlayFabHttp.SimplePutCall(response.UploadDetails[0].UploadUrl,
            payload,
            FinalizeUpload,
            error => { Debug.Log(error); }
        );
        GlobalFileLock -= 1; // Finish InitiateFileUploads
    }
    void FinalizeUpload(byte[] body) // body is unused in this example
    {
        GlobalFileLock += 1; // Start FinalizeFileUploads
        var request = new PlayFab.DataModels.FinalizeFileUploadsRequest
        {
            Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
            FileNames = new List<string> { ActiveUploadFileName },
        };
        PlayFabDataAPI.FinalizeFileUploads(request, OnUploadSuccess, OnSharedFailure);
        GlobalFileLock -= 1; // Finish SimplePutCall
    }
    void OnUploadSuccess(PlayFab.DataModels.FinalizeFileUploadsResponse result)
    {
        Debug.Log("File upload success: " + ActiveUploadFileName);
        GlobalFileLock -= 1; // Finish FinalizeFileUploads
    }
}
#endif
```

## 解析此範例

* `GlobalFileLock` 是一種避免檔案衝突的簡單方法，專為此範例設計。
  * 獨立的檔案動作不會造成任何問題。
  * 每個檔案動作都需要許多步驟和多次 API 呼叫，因此請勿嘗試同時以多種方式存取同一個檔案。
  * 如果您夠謹慎，就不需要任何鎖定機制。
  * 如果您想進行複雜的操作，您的鎖定機制可能會複雜得多。
* `OnGUI` 是一種舊式但非常精簡的方法，可完全在指令碼中建置 Unity GUI。
  * 您的 GUI 會好得多，而且會針對遊戲量身打造。
* 所有 PlayFab 功能都*必須先*登入或驗證。
* `LoadAllFiles()` 正如其名。針對目前登入的實體，它會載入儲存至 PlayFab 的所有檔案。
  * 此函式需要多個步驟：
    * 詢問 PlayFab 檔案的位置，
    * 然後分別下載檔案。
* `UploadFile(string fileName)` 會將實體的檔案儲存至服務。
  * 為了簡單起見，此範例一次儲存一個檔案，但您也可以成組地以不可分割的方式上傳檔案。
  * 此作業的步驟如下：
    * 初始化不可分割的上傳作業，
    * 上傳所有檔案，
    * 完成不可分割的上傳作業。
  * 在不可分割的上傳作業成功完成之前，實體不會將檔案上傳視為已完成，也不會向其他呼叫端反映任何變更。

## Game Manager 和實體

Game Manager 可讓您操作玩家的物件和檔案。玩家概觀已更新，可同時顯示遊戲玩家帳戶和主要玩家帳戶資訊。

<img src="https://mintcdn.com/microsoft-4404708b/3hg2JQs0m7qmDqay/images/playfab/live-service-management/game-configuration/entities/tutorials/game-manager-entities-player-overview.png?fit=max&auto=format&n=3hg2JQs0m7qmDqay&q=85&s=ceac36d3b1e7fd248caeedbf4bea8055" alt="Game Manager - 實體 - 玩家概觀" width="1800" height="1406" data-path="images/playfab/live-service-management/game-configuration/entities/tutorials/game-manager-entities-player-overview.png" />

此外，**Players** 索引標籤現在包含檔案和物件的個別區段。

<img src="https://mintcdn.com/microsoft-4404708b/3hg2JQs0m7qmDqay/images/playfab/live-service-management/game-configuration/entities/tutorials/game-manager-entities-player-files.png?fit=max&auto=format&n=3hg2JQs0m7qmDqay&q=85&s=70b96669460d8bb673ec25fdbcd1bd41" alt="Game Manager - 實體 - 玩家檔案和物件" width="1800" height="969" data-path="images/playfab/live-service-management/game-configuration/entities/tutorials/game-manager-entities-player-files.png" />

## 相關功能

實體物件和檔案用於多項 PlayFab 功能，每項功能都限定於不同的實體類型：

| 功能 | 實體類型 | 使用案例 |
| - | - | - |
| [遊戲伺服器設定](/zh-TW/services/playfab/live-service-management/game-configuration) | `game_server` | 伺服器端設定和伺服器授權的資料 |
| [遊戲資料](/zh-TW/services/playfab/live-service-management/game-configuration/titledata) | `namespace`, `title` | 所有玩家共用的全域索引鍵/值設定 |
| [玩家資料](/zh-TW/services/playfab/player-progression/player-data) | `title_player_account`, `master_player_account`, `character` | 每位玩家的設定檔、進度和喜好設定儲存 |
| [群組](/zh-TW/services/playfab/community/associations/groups) | `group` | 在玩家群組或公會內共用的資料 |


## Related topics

- [實體快速入門](/zh-TW/services/playfab/live-service-management/game-configuration/entities/quickstart.md)
- [PlayFab GDPR - 刪除玩家資料](/zh-TW/services/playfab/data-analytics/privacy-compliance/gdpr-deleting-player-data.md)
- [PlayFab GDPR - 匯出玩家資料](/zh-TW/services/playfab/data-analytics/privacy-compliance/gdpr-exporting-player-data.md)
- [遊戲伺服器設定概觀](/zh-TW/services/playfab/live-service-management/game-configuration/index.md)
- [PFProfilesEntityProfileFileMetadata](/zh-TW/services/playfab/api-references/c/pfprofilestypes/structs/pfprofilesentityprofilefilemetadata.md)
