using PlayFab; using PlayFab.AdminModels; using PlayFab.ClientModels; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; using UnityEngine; using UnityEngine.Events; /// /// The out of the box WWW Unity3d class does not afford compatable PUT capability with AWS /// This class provides a simple example for how to upload files to PlayFab's content service using .NET's built-in HttpWebRequest. /// /// Scenario: I have a file (UB_Icon.png) located in my /Assets/StreamingAssets project folder that I need to upload to the PlayFab content service and download at a later time for use within my client. /// public class UploadToPlayFabContentService : MonoBehaviour { public bool cleanCacheOnStart = false; public bool isInitalContentUnpacked = false; public bool useCDN = true; public Texture2D defaultBanner; public Texture2D defaultSplash; public List assets; public Dictionary unpackedAssets = new Dictionary(); // Use this for initialization void Start() { if (cleanCacheOnStart) { #if UNITY_2017_1_OR_NEWER Caching.ClearCache(); #else Caching.CleanCache(); #endif } foreach (var asset in assets) { if (!asset.IsFlagedForUpload) continue; StartCoroutine(GetFilePath(asset)); GetContentUploadURL(asset); } } #region PUT_Methods /// /// Requests a remote endpoint for uploads from the PlaFab service. /// void GetContentUploadURL(AssetBundleHelperObject asset) { var request = new GetContentUploadUrlRequest(); if (asset.BundlePlatform == AssetBundleHelperObject.BundleTypes.Android) request.Key = "Android/" + asset.ContentKey; // folder location & file name to use on the remote server else if (asset.BundlePlatform == AssetBundleHelperObject.BundleTypes.iOS) request.Key = "iOS/" + asset.ContentKey; else // stand-alone request.Key = asset.ContentKey; request.ContentType = asset.MimeType; // mime type to match the file #if UNITY_WEBPLAYER //UnityEngine.Deubg.Log("Webplayer does not support uploading files."); #else PlayFabAdminAPI.GetContentUploadUrl(request, result => { asset.PutUrl = result.URL; byte[] fileContents = File.ReadAllBytes(asset.LocalPutPath); PutFile(asset, fileContents); }, OnPlayFabError); #endif } /// /// Puts the file. /// /// Remote URL to use (obtained from GetContentUploadUrl) /// The file to send converted to a byte[] public void PutFile(AssetBundleHelperObject asset, byte[] payload) { if (payload == null) { Debug.LogWarning("ERROR: Byte arrry was empty or null"); return; } var request = (HttpWebRequest)WebRequest.Create(asset.PutUrl); request.Method = "PUT"; request.ContentType = asset.MimeType; using (var dataStream = request.GetRequestStream()) dataStream.Write(payload, 0, payload.Length); var response = (HttpWebResponse)request.GetResponse(); if (response.StatusCode != HttpStatusCode.OK) Debug.LogWarning(string.Format("ERROR: Asset:{0} -- Code:[{1}] -- Msg:{2}", asset.FileName, response.StatusCode, response.StatusDescription)); } #endregion #region GET_Methods public void KickOffCDNGet(List assets, UnityAction callback = null) { StartCoroutine(GetDownloadEndpoints(assets, callback)); } public void KickOffStreamingAssetsGet(List assets, UnityAction callback = null) { var path = Application.streamingAssetsPath; var keyPrefix = string.Empty; foreach (var asset in assets) { switch (asset.BundlePlatform) { case AssetBundleHelperObject.BundleTypes.Android: keyPrefix = "Android/"; break; case AssetBundleHelperObject.BundleTypes.iOS: keyPrefix = "iOS/"; break; } asset.GetUrl = string.Format("file://{0}/{1}{2}", path, keyPrefix, asset.ContentKey); Debug.LogWarningFormat("Get At: {0}",asset.GetUrl); } StartCoroutine(GetAssetPackages(assets, callback)); } public IEnumerator GetDownloadEndpoints(List assets, UnityAction callback = null) { var endTime = Time.time + 30.0f; foreach (var asset in assets) GetContentDownloadURL(asset); while (DoAllAssetsHaveDownloadEndpoints(assets) == false) { if (Time.time > endTime) { Debug.Log("Error: TimeOut"); PF_Bridge.RaiseCallbackError("CDN Timeout: Could not obtain endpoints", PlayFabAPIMethods.GetCDNConent, MessageDisplayStyle.error); yield break; } yield return 0; } StartCoroutine(GetAssetPackages(assets, callback)); } /// /// Requests a remote endpoint for downloads from the PlaFab service. /// void GetContentDownloadURL(AssetBundleHelperObject asset) { var request = new GetContentDownloadUrlRequest(); if (asset.BundlePlatform == AssetBundleHelperObject.BundleTypes.Android) request.Key = "Android/" + asset.ContentKey; // folder location & file name to use on the remote server else if (asset.BundlePlatform == AssetBundleHelperObject.BundleTypes.iOS) request.Key = "iOS/" + asset.ContentKey; else // stand-alone request.Key = asset.ContentKey; PlayFabClientAPI.GetContentDownloadUrl(request, result => { asset.GetUrl = result.URL; }, OnPlayFabError); } public IEnumerator GetAssetPackages(List assets, UnityAction callback = null) { var stTime = Time.time; var timeOut = 30.0f; foreach (var asset in assets) StartCoroutine(DownloadAndUnpackAsset(asset)); while (AreAssetDownloadsAndUnpacksComplete(assets) == false) { if (Time.time > stTime + timeOut) { PF_Bridge.RaiseCallbackError("CDN Timeout: Could not obtain AssetBundles", PlayFabAPIMethods.GetCDNConent, MessageDisplayStyle.error); if (callback != null) callback(false); yield break; } yield return 0; } foreach (var asset in assets) unpackedAssets.Add(asset.FileName, asset.Unpacked); if (callback != null) callback(true); Debug.Log("--- AllComplete ---"); } public IEnumerator DownloadAndUnpackAsset(AssetBundleHelperObject asset) { // Caching.IsVersionCached(asset.GetUrl, asset.Version) // Start a download of the given URL var www = WWW.LoadFromCacheOrDownload(asset.GetUrl, asset.Version); // wait until the download is done while (www.progress < 1) { asset.progress = www.progress; yield return www; } if (!string.IsNullOrEmpty(www.error)) { asset.Error = www.error; Debug.LogError("HTTP ERROR:" + asset.ContentKey + "\n" + www.error); yield break; } asset.Error = ""; asset.Unpacked.ContentKey = asset.ContentKey; asset.Unpacked.PromoId = asset.FileName; asset.Bundle = www.assetBundle; var assetNames = asset.Bundle.GetAllAssetNames(); foreach (var assetName in assetNames) { var bannerUri = string.Empty; var splashUri = string.Empty; var assetNameLc = assetName.ToLower(); var isImage = assetNameLc.EndsWith(".jpg") || assetNameLc.EndsWith(".png"); if (assetName.ToLower().Contains("banner.") && isImage) bannerUri = assetName; else if (assetName.ToLower().Contains("splash.") && isImage) splashUri = assetName; if (string.IsNullOrEmpty(bannerUri) == false) asset.Unpacked.Banner = asset.Bundle.LoadAsset(bannerUri); else if (string.IsNullOrEmpty(splashUri) == false) asset.Unpacked.Splash = asset.Bundle.LoadAsset(splashUri); else asset.Error += string.Format("[Err: Unpacking: {0} -- {1} ]", asset.FileName, assetName); } asset.Bundle.Unload(false); asset.IsUnpacked = true; } #endregion #region Helper_Methods /// /// Gets the reletive file path; works acrocss Unity build targets (Web, iOS, Android, PC, Mac) /// /// The assetPath where the file can be found (will varry depending on the platform) IEnumerator GetFilePath(AssetBundleHelperObject asset) { var platformPrefix = "/"; if (asset.BundlePlatform == AssetBundleHelperObject.BundleTypes.Android) platformPrefix = "/Android/"; // folder location & file name to use on the remote server else if (asset.BundlePlatform == AssetBundleHelperObject.BundleTypes.iOS) platformPrefix = "/iOS/"; var streamingAssetPath = Application.streamingAssetsPath; //string filePath = System.IO.Path.Combine(streamingAssetPath, asset.FileName); var filePath = streamingAssetPath + platformPrefix + asset.FileName; //useful for uploading from crossplatform (iOS / Android) clients if (filePath.Contains("://")) { var www = new WWW(filePath); yield return www; asset.LocalPutPath = www.text; } else { asset.LocalPutPath = filePath; } } public bool DoAllAssetsHaveDownloadEndpoints(List assets) { foreach (var asset in assets) if (string.IsNullOrEmpty(asset.GetUrl)) return false; return true; } public bool AreAssetDownloadsAndUnpacksComplete(List assets) { foreach (var asset in assets) if (asset.IsUnpacked == false) return false; return true; } // need a way to serve up assets by id now. public UB_UnpackedAssetBundle GetAssetsByID(string id) { if (unpackedAssets.ContainsKey(id)) { if (unpackedAssets[id].Splash == null) unpackedAssets[id].Splash = defaultSplash; if (unpackedAssets[id].Banner == null) unpackedAssets[id].Splash = defaultBanner; return unpackedAssets[id]; } else { var obj = new UB_UnpackedAssetBundle { ContentKey = "Default", PromoId = "Default", Splash = defaultSplash, Banner = defaultBanner }; return obj; } } /// /// Called after a failed GetContentUploadUrl request /// /// Error details void OnPlayFabError(PlayFabError error) { Debug.LogWarning(string.Format("PLAYFAB ERROR: [{0}] -- {1}", error.Error, error.ErrorMessage)); } #endregion } [Serializable] public class AssetBundleHelperObject { public string FileName; public string ContentKey; public int Version; public string MimeType; public BundleTypes BundlePlatform; public string LocalPutPath; public string PutUrl; public string GetUrl; public string Error; public float progress; public AssetBundle Bundle; public bool IsUnpacked; public bool IsFlagedForUpload; public UB_UnpackedAssetBundle Unpacked; //public enum MimeTypes { application/x-gzip, application/octet-stream} public enum BundleTypes { StandAlone, iOS, Android } } [Serializable] public class UB_UnpackedAssetBundle { public string PromoId; public string ContentKey; public Texture2D Banner; public Texture2D Splash; }