如何透過Unity上傳文件, 圖片, 影片, 模型到GCP Cloud Storage, 並且取得存取網址。
第一步:GCP 事前准備
a. 註冊號GCP和填好付款資訊之後,打開Cloud Storage服務
b. 跟著引導步驟新增一個新的Bucket (可以理解成一個存放檔案的文件夾)
c. 去到權限頁面,新增一個allUsers的儲存空間檢視者用戶
d. 這裡可以快速切換要不要把這個儲存空間對外公開(我們先把它公開)
e. 你可以在這裡嘗試上傳一些文件檔案看看
f. 接下來,去打開IAM與管理的服務
g. 打開服務賬戶標籤,點擊建立服務賬戶
h. 根據指示輸入一個名字,將角色設成 Storage 管理員
i. 建立好後,點擊打開,去到金鑰頁面,新增一個金鑰,並且把Json檔案下載下來。
GCP 的初始設定到這裡就告一段落了,接下來我們去Unity實戰 !
第二步: Unity安裝GCP服務
a. 下載 Nuget For Unity
b. 下載好後,在Unity中打開,搜尋Google GCP的套件安裝
我們要安裝 Google.Apis 和 Google.Cloud.Storage.V1 的套件
c. 接下來把我們剛才下載的Json檔案放到Streaming Asset之中
做到這裡基本的環境架設就弄好了,接下來可以開始寫Code啦。
第三步: Unity 上傳文件
完整的代碼我會放在下面和提供UnityPackage,我們先來小部分拆解代碼:
a. 先寫一個ICloudStorage 的Interface
包含3個Function:
- Initialize – 帶入GCP 的 BucketName 和 Json的路徑
- UploadObject – 上傳本地檔案的Function
- UploadObjectByUrl – 直接上傳網絡URL檔案的Function
b. 寫一個CStorageImp Class繼承這個Interface:
c. 初始化StorageClient的代碼(這個是GCP內建的Class)
d. 上傳文件到GCP Cloud Storage的Function
e. 從本地文件路徑上傳就開File讀取
f. 從URL上傳的話就用WebRequest先抓一下
g. 最後把Interface的Function接好
using System;
namespace YFrame.API.Google.CloudStorage
{
public interface ICloudStorage
{
public void Initialize(string bucketName, string jsonCrePath);
public void UploadObject(string localFilePath, string remoteFilePath, Action<double> progressAction);
public void UploadObjectByUrl(string fileUrl, string remoteFilePath, Action<double> progressAction);
}
}
using Google.Cloud.Storage.V1;
using System;
using System.IO;
using System.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Upload;
using UnityEngine;
using UnityEngine.Networking;
using YFrame.Utility;
namespace YFrame.API.Google.CloudStorage
{
public class CStorageImp : ICloudStorage
{
public string BucketName = "your-bucket-name";
public string JsonCrePath = "path-to-your-service-account-json-key-file";
private StorageClient storageClient;
public void Initialize(string bucketName, string jsonCrePath)
{
BucketName = bucketName;
JsonCrePath = jsonCrePath;
InitializeStorageClient();
}
private void InitializeStorageClient()
{
try
{
GoogleCredential credential;
using (var jsonStream = new FileStream(JsonCrePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
credential = GoogleCredential.FromStream(jsonStream);
}
storageClient = StorageClient.Create(credential);
}
catch (Exception ex)
{
Debug.LogError($"GCP Cloud Storage: Error initializing storage client: {ex.Message}");
}
}
public async void UploadObject(string localFilePath, string remoteFilePath, Action<double> progressAction)
{
await UploadFile(localFilePath, remoteFilePath, progressAction);
}
public async void UploadObjectByUrl(string fileUrl, string remoteFilePath, Action<double> progressAction)
{
await UploadFromUrl(fileUrl, remoteFilePath, progressAction);
}
private async Task UploadFile(string localFilePath, string remoteFileName, Action<double> progressAction)
{
try
{
await using var fileStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);
await UploadStream(fileStream, remoteFileName, progressAction);
}
catch (Exception ex)
{
Debug.LogError($"GCP Cloud Storage: Error uploading file: {ex.Message}");
}
}
private async Task UploadFromUrl(string url, string remoteFileName, Action<double> progressAction)
{
using var www = UnityWebRequest.Get(url);
www.SendWebRequest();
while (!www.isDone)
{
await Task.Yield();
}
if (www.result != UnityWebRequest.Result.Success)
{
throw new Exception($"GCP Cloud Storage: Failed to download file: {www.error}");
}
using var memoryStream = new MemoryStream(www.downloadHandler.data);
await UploadStream(memoryStream, remoteFileName, progressAction);
}
private async Task UploadStream(Stream stream, string remoteFileName, Action<double> progressAction)
{
var progress = new Progress<IUploadProgress>(p =>
{
var percentComplete = (double)p.BytesSent / stream.Length * 100;
Debug.Log($"GCP Cloud Storage Upload Progress: " +
$"{percentComplete:F2}% ({p.BytesSent}/{stream.Length} bytes)");
// Update UI Action on main thread
if (progressAction != null)
{
UnityMainThreadDispatcher.Instance().Enqueue(() => { progressAction?.Invoke(percentComplete); });
}
});
var uploadObjectOptions = new UploadObjectOptions
{
ChunkSize = UploadObjectOptions.MinimumChunkSize
};
await storageClient.UploadObjectAsync(
BucketName,
remoteFileName,
null,
stream,
options: uploadObjectOptions,
progress: progress
);
Debug.Log($"GCP Cloud Storage: Success Uploaded {remoteFileName} to {BucketName}.");
}
}
}
第四步: 最終調用
a. 新建一個簡單的Demo Scene場景進行測試
包含的UI元件:
- Dropdown – 可以切換本地上傳 / URL上傳
- Local File InputField – 要上傳的文件路徑
- Remote File Input Field – GCP Cloud Storage 上的儲存路徑名字
- 上傳按鈕
- 上傳進度顯示文字
b. 新增一個Cloud Storage View 的Mono Behavior 代碼:
記得一開始要初始化CloudStorage的函數,把BucketName和Json路徑帶進去
c. 按鈕點擊的時候呼叫CloudStorage的UploadObject即可
簡單乾淨!
接著打開你的Scene,在Streaming Asset放一些測試圖片,上傳測試看看即可。
上傳成功之後,可以去GCP Bucket上刷新看看有沒有出現。
using System.IO;
using UnityEngine;
using UnityEngine.UI;
namespace YFrame.API.Google.CloudStorage.Scene
{
public class CloudStorageView : MonoBehaviour
{
public string bucketName = "your-bucket-name";
public string jsonKeyFilePath = "path-to-your-service-account-json-key-file";
[Header("File Upload UI")]
public Dropdown UploadTypeDropdown;
public Button UploadBtn;
public Text UploadProgressText;
public InputField FileInputField;
public InputField RemoteFileNameInputField;
private ICloudStorage CloudStorage = new CStorageImp();
private void Start()
{
CloudStorage.Initialize(bucketName, Path.Combine(Application.streamingAssetsPath, jsonKeyFilePath));
UploadBtn.onClick.AddListener(UploadBtnOnClick);
}
private void UploadBtnOnClick()
{
switch (UploadTypeDropdown.value)
{
// Streaming Local File
case 0:
{
var filePath = Path.Combine(Application.streamingAssetsPath, FileInputField.text);
CloudStorage.UploadObject(filePath, RemoteFileNameInputField.text, UpdateProgressText);
return;
}
// Upload By URL
case 1:
{
CloudStorage.UploadObjectByUrl(FileInputField.text, RemoteFileNameInputField.text, UpdateProgressText);
break;
}
}
}
private void UpdateProgressText(double progress)
{
UploadProgressText.text = $"Upload Progress: {progress:F2}";
}
}
}
效果展示
完成的Unity Package包下載:
上传一直是0%