├── .gitignore ├── BlobService.cs ├── LICENSE ├── README.md ├── StorageServiceClient.cs ├── http ├── StorageRequest.cs ├── auth │ ├── Auth.cs │ └── headers │ │ ├── AuthorizationHeaders.cs │ │ └── CanonicalizedHeaders.cs └── query │ └── ResType.cs └── model ├── BlobResults.cs ├── ContainerResults.cs └── ErrorResult.cs /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /Assets/AssetStoreTools* 7 | 8 | # Visual Studio 2015 cache directory 9 | /.vs/ 10 | 11 | # Autogenerated VS/MD/Consulo solution and project files 12 | ExportedObj/ 13 | .consulo/ 14 | *.csproj 15 | *.unityproj 16 | *.sln 17 | *.suo 18 | *.tmp 19 | *.user 20 | *.userprefs 21 | *.pidb 22 | *.booproj 23 | *.svd 24 | *.pdb 25 | 26 | # Unity3D generated meta files 27 | *.pidb.meta 28 | *.meta 29 | 30 | # Unity3D Generated File On Crash Reports 31 | sysinfo.txt 32 | 33 | # Builds 34 | *.apk 35 | *.unitypackage 36 | 37 | # IDEs 38 | /.vscode 39 | omnisharp.json 40 | .editorconfig 41 | -------------------------------------------------------------------------------- /BlobService.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using RESTClient; 5 | using System.Collections; 6 | using System; 7 | using System.Collections.Generic; 8 | using UnityEngine; 9 | using System.IO; 10 | using System.Text; 11 | 12 | namespace Azure.StorageServices { 13 | public class BlobService { 14 | private StorageServiceClient client; 15 | 16 | public BlobService(StorageServiceClient client) { 17 | this.client = client; 18 | } 19 | 20 | /// 21 | /// Lists all of the containers in a storage account. 22 | /// 23 | /// The containers. 24 | /// . 25 | public IEnumerator ListContainers(Action> callback) { 26 | Dictionary queryParams = new Dictionary(); 27 | queryParams.Add("comp", "list"); 28 | queryParams.Add("restype", ResType.container.ToString()); 29 | StorageRequest request = Auth.CreateAuthorizedStorageRequest(client, Method.GET, "", queryParams); 30 | yield return request.Send(); 31 | request.ParseXML(callback); 32 | } 33 | 34 | /// 35 | /// Lists all of the blobs in a container. 36 | /// 37 | /// The containers. 38 | /// . 39 | public IEnumerator ListBlobs(Action> callback, string resourcePath = "") { 40 | Dictionary queryParams = new Dictionary(); 41 | queryParams.Add("comp", "list"); 42 | queryParams.Add("restype", ResType.container.ToString()); 43 | StorageRequest request = Auth.CreateAuthorizedStorageRequest(client, Method.GET, resourcePath, queryParams); 44 | yield return request.Send(); 45 | request.ParseXML(callback); 46 | } 47 | 48 | #region Download blobs 49 | 50 | public IEnumerator GetTextBlob(Action callback, string resourcePath = "") { 51 | StorageRequest request = Auth.GetAuthorizedStorageRequest(client, resourcePath); 52 | yield return request.Send(); 53 | request.GetText(callback); 54 | } 55 | 56 | public IEnumerator GetJsonBlob(Action> callback, string resourcePath = "") { 57 | StorageRequest request = Auth.GetAuthorizedStorageRequest(client, resourcePath); 58 | yield return request.Send(); 59 | request.ParseJson(callback); 60 | } 61 | 62 | public IEnumerator GetJsonArrayBlob(Action> callback, string resourcePath = "") { 63 | StorageRequest request = Auth.GetAuthorizedStorageRequest(client, resourcePath); 64 | yield return request.Send(); 65 | request.ParseJsonArray(callback); 66 | } 67 | 68 | public IEnumerator GetXmlBlob(Action> callback, string resourcePath = "") { 69 | StorageRequest request = Auth.GetAuthorizedStorageRequest(client, resourcePath); 70 | yield return request.Send(); 71 | request.ParseXML(callback); 72 | } 73 | 74 | public IEnumerator GetImageBlob(Action> callback, string resourcePath = "") { 75 | StorageRequest request = Auth.GetAuthorizedStorageRequestTexture(client, resourcePath); 76 | yield return request.Send(); 77 | request.GetTexture(callback); 78 | } 79 | 80 | public IEnumerator GetAudioBlob(Action> callback, string resourcePath = "") { 81 | StorageRequest request = Auth.GetAuthorizedStorageRequestAudioClip(client, resourcePath); 82 | yield return request.Send(); 83 | request.GetAudioClip(callback); 84 | } 85 | 86 | public IEnumerator GetAssetBundle(Action> callback, string resourcePath = "") { 87 | StorageRequest request = Auth.GetAuthorizedStorageRequestAssetBundle(client, resourcePath); 88 | yield return request.Send(); 89 | request.GetAssetBundle(callback); 90 | } 91 | 92 | public IEnumerator GetBlob(Action> callback, string resourcePath = "") { 93 | StorageRequest request = Auth.GetAuthorizedStorageRequest(client, resourcePath); 94 | yield return request.Send(); 95 | request.GetBytes(callback); 96 | } 97 | 98 | #endregion 99 | 100 | #region Upload blobs 101 | 102 | public IEnumerator PutTextBlob(Action callback, string text, string resourcePath, string filename, string contentType = "text/plain; charset=UTF-8") { 103 | byte[] bytes = Encoding.UTF8.GetBytes(text); 104 | return PutBlob(callback, bytes, resourcePath, filename, contentType); 105 | } 106 | 107 | public IEnumerator PutImageBlob(Action callback, byte[] bytes, string resourcePath, string filename, string contentType = "image/png") { 108 | return PutBlob(callback, bytes, resourcePath, filename, contentType); 109 | } 110 | 111 | public IEnumerator PutAudioBlob(Action callback, byte[] bytes, string resourcePath, string filename, string contentType = "audio/wav") { 112 | return PutBlob(callback, bytes, resourcePath, filename, contentType); 113 | } 114 | 115 | public IEnumerator PutAssetBundle(Action callback, byte[] bytes, string resourcePath, string filename, string contentType = "application/octet-stream") { 116 | return PutBlob(callback, bytes, resourcePath, filename, contentType); 117 | } 118 | 119 | public IEnumerator PutBlob(Action callback, byte[] bytes, string resourcePath, string filename, string contentType, Method method = Method.PUT) { 120 | int contentLength = bytes.Length; // TODO: check size is ok? 121 | Dictionary headers = new Dictionary(); 122 | string file = Path.GetFileName(filename); 123 | 124 | headers.Add("Content-Type", contentType); 125 | headers.Add("x-ms-blob-content-disposition", string.Format("attachment; filename=\"{0}\"", file)); 126 | headers.Add("x-ms-blob-type", "BlockBlob"); 127 | 128 | string filePath = resourcePath.Length > 0 ? resourcePath + "/" + file : file; 129 | StorageRequest request = Auth.CreateAuthorizedStorageRequest(client, method, filePath, null, headers, contentLength); 130 | request.AddBody(bytes, contentType); 131 | yield return request.Send(); 132 | request.Result(callback); 133 | } 134 | 135 | #endregion 136 | 137 | public IEnumerator DeleteBlob(Action callback, string resourcePath, string filename) { 138 | string filePath = resourcePath.Length > 0 ? resourcePath + "/" + filename : filename; 139 | StorageRequest request = Auth.CreateAuthorizedStorageRequest(client, Method.DELETE, filePath); 140 | yield return request.Send(); 141 | request.Result(callback); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Unity3dAzure 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure Storage Services for Unity3d 2 | 3 | Save and load image textures, audio files, json or xml data files for use in 4 | Unity. You can also store Unity Asset Bundles to load Prefabs with referenced 5 | scripts to work in your game / application. 6 | 7 | ## External dependencies 8 | 9 | **First download the shared 10 | [REST Client library for Unity](https://github.com/Unity3dAzure/RESTClient) and 11 | extract the contents into your Unity project "Assets" folder.** 12 | 13 | * [RESTClient](https://github.com/Unity3dAzure/RESTClient) 14 | 15 | ## Requirements 16 | 17 | Unity 2017.2 recommended. Unity v5.3 or greater required as 18 | [UnityWebRequest](https://docs.unity3d.com/Manual/UnityWebRequest.html) and 19 | [JsonUtility](https://docs.unity3d.com/ScriptReference/JsonUtility.html) 20 | features are used. Unity will be extending platform support for UnityWebRequest 21 | so keep Unity up to date if you need to support these additional platforms. 22 | 23 | ## Azure Blob Storage Demos for Unity 2017.2 24 | 25 | Try the 26 | [Azure Storage Services Demos](https://github.com/Unity3dAzure/StorageServicesDemo) 27 | project for Unity on Mac / Windows. (The demo project has got everything already 28 | bundled in and does not require any additional assets to work. Just wire it up 29 | with your Azure Storage Service and public Blob container and run it right 30 | inside the Unity Editor.) 31 | 32 | ## How to setup Storage Services with a new Unity project 33 | 34 | 1. [Download StorageServices](https://github.com/Unity3dAzure/StorageServices/archive/master.zip) 35 | and 36 | [REST Client](https://github.com/Unity3dAzure/RESTClient/archive/master.zip) 37 | for Unity. 38 | * Copy 'StorageServices' and 'RESTClient' into your Unity project's `Assets` 39 | folder. 40 | 2. Create [Azure](https://portal.azure.com) Storage Service 41 | 42 | ## Supported platforms 43 | 44 | Intended to work on all the platforms 45 | [UnityWebRequest](https://docs.unity3d.com/Manual/UnityWebRequest.html) supports 46 | including: 47 | 48 | * Unity Editor and Standalone players 49 | * iOS 50 | * Android 51 | * Windows 52 | 53 | ## Notice 54 | 55 | This library is in beta so not all APIs are supported yet and some things may 56 | change. 57 | 58 | Questions or tweet [@deadlyfingers](https://twitter.com/deadlyfingers) 59 | -------------------------------------------------------------------------------- /StorageServiceClient.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using RESTClient; 5 | using System; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace Azure.StorageServices { 9 | public sealed class StorageServiceClient : RestClient { 10 | private string account; 11 | 12 | public string Account { 13 | get { 14 | return account; 15 | } 16 | } 17 | 18 | private byte[] key; 19 | 20 | public byte[] Key { 21 | get { 22 | return key; 23 | } 24 | } 25 | 26 | // https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/versioning-for-the-azure-storage-services 27 | private string version; 28 | 29 | public string Version { 30 | get { 31 | return version; 32 | } 33 | } 34 | 35 | public StorageServiceClient(string url, string accessKey, string version = "2017-04-17", string account = "") : base(url) { 36 | this.version = version; 37 | if (!string.IsNullOrEmpty(accessKey)) { 38 | this.key = Convert.FromBase64String(accessKey); 39 | } 40 | if (!string.IsNullOrEmpty(account)) { 41 | this.account = account; 42 | } else { 43 | this.account = GetAccountName(url); 44 | } 45 | } 46 | 47 | /// 48 | /// Creates a new instance of the class. 49 | /// 50 | /// Storage account name. 51 | /// Access key. 52 | public static StorageServiceClient Create(string account, string accessKey, string version = "2017-04-17") { 53 | string url = GetPrimaryEndpoint(account); 54 | return new StorageServiceClient(url, accessKey, version, account); 55 | } 56 | 57 | public BlobService GetBlobService() { 58 | return new BlobService(this); 59 | } 60 | 61 | public string PrimaryEndpoint() { 62 | return GetPrimaryEndpoint(account); 63 | } 64 | 65 | public string SecondaryEndpoint() { 66 | return GetSecondaryEndpoint(account); 67 | } 68 | 69 | private static string GetPrimaryEndpoint(string account) { 70 | return "https://" + account + ".blob.core.windows.net/"; 71 | } 72 | 73 | private static string GetSecondaryEndpoint(string account) { 74 | return "https://" + account + ".blob.core.windows.net/"; 75 | } 76 | 77 | private string GetAccountName(string url) { 78 | var match = Regex.Match(url, @"^https?:\/\/([a-z0-9]+)", RegexOptions.IgnoreCase); 79 | if (match.Groups.Count == 2 && match.Groups[1].Value.Length > 0) { 80 | return match.Groups[1].Value; 81 | } 82 | return url; 83 | } 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /http/StorageRequest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using RESTClient; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Text; 8 | using UnityEngine; 9 | using UnityEngine.Networking; 10 | 11 | namespace Azure.StorageServices { 12 | public sealed class StorageRequest : RestRequest { 13 | public StorageRequest(string url, Method method) : base(url, method) { 14 | } 15 | 16 | public StorageRequest(UnityWebRequest request) : base(request) { 17 | } 18 | 19 | public void AuthorizeRequest(StorageServiceClient client, Method method, string resourcePath = "", Dictionary queryParams = null, Dictionary headers = null, int contentLength = 0) { 20 | AuthorizationHeaders authHeaders = new AuthorizationHeaders(client, method, resourcePath, queryParams, headers, contentLength); 21 | string stringToSign = authHeaders.ToString(); 22 | string signature = GetSignature(client.Key, stringToSign); 23 | string authorization = GetAuthorizationHeader(client.Account, signature); 24 | 25 | this.AddHeader("Authorization", authorization); 26 | this.AddHeader("x-ms-date", authHeaders.MSDate()); 27 | this.AddHeader("x-ms-version", authHeaders.MSVersion()); 28 | 29 | if (headers != null) { 30 | this.AddHeaders(headers); 31 | } 32 | 33 | Debug.Log("Authorized request url:" + this.Request.url + "\n\nauthorization: \"" + authorization + "\"\nx-ms-date: " + authHeaders.MSDate() + "\nstringToSign:'" + stringToSign + "'"); 34 | } 35 | 36 | /// 37 | /// Creates Signature using format Base64(HMAC-SHA256(UTF8(StringToSign))) 38 | /// 39 | /// The signature. 40 | /// String to sign. 41 | private string GetSignature(byte[] key, string stringToSign) { 42 | return SignatureHelper.Sign(key, stringToSign); 43 | } 44 | 45 | /// 46 | /// Authorization header value 47 | /// 48 | /// Account. 49 | /// Signature. 50 | private string GetAuthorizationHeader(string account, string signature) { 51 | return string.Format("SharedKey {0}:{1}", account, signature); 52 | } 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /http/auth/Auth.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using RESTClient; 5 | using System.Collections.Generic; 6 | using System.Security.Cryptography; 7 | using UnityEngine; 8 | using System.Text; 9 | using System; 10 | using UnityEngine.Networking; 11 | 12 | namespace Azure.StorageServices { 13 | public static class Auth { 14 | /// 15 | /// Factory method to generate an authorized request URL using query params. (valid up to 15 minutes) 16 | /// 17 | /// The authorized request. 18 | /// StorageServiceClient 19 | /// Http method. 20 | public static StorageRequest CreateAuthorizedStorageRequest(StorageServiceClient client, Method method, string resourcePath = "", Dictionary queryParams = null, Dictionary headers = null, int contentLength = 0) { 21 | string requestUrl = RequestUrl(client, queryParams, resourcePath); 22 | StorageRequest request = new StorageRequest(requestUrl, method); 23 | request.AuthorizeRequest(client, method, resourcePath, queryParams, headers, contentLength); 24 | return request; 25 | } 26 | 27 | public static StorageRequest GetAuthorizedStorageRequest(StorageServiceClient client, string resourcePath = "", Dictionary queryParams = null, Dictionary headers = null, int contentLength = 0) { 28 | return CreateAuthorizedStorageRequest(client, Method.GET, resourcePath, queryParams, headers, contentLength); 29 | } 30 | 31 | public static StorageRequest GetAuthorizedStorageRequestTexture(StorageServiceClient client, string resourcePath = "", Dictionary queryParams = null, Dictionary headers = null, int contentLength = 0) { 32 | string requestUrl = RequestUrl(client, queryParams, resourcePath); 33 | StorageRequest request = new StorageRequest(UnityWebRequestTexture.GetTexture(requestUrl)); 34 | request.AuthorizeRequest(client, Method.GET, resourcePath, queryParams, headers, contentLength); 35 | return request; 36 | } 37 | 38 | public static StorageRequest GetAuthorizedStorageRequestAudioClip(StorageServiceClient client, string resourcePath = "", Dictionary queryParams = null, Dictionary headers = null, int contentLength = 0, AudioType audioType = AudioType.WAV) { 39 | string requestUrl = RequestUrl(client, queryParams, resourcePath); 40 | StorageRequest request = new StorageRequest(UnityWebRequestMultimedia.GetAudioClip(requestUrl, audioType)); 41 | request.AuthorizeRequest(client, Method.GET, resourcePath, queryParams, headers, contentLength); 42 | return request; 43 | } 44 | 45 | public static StorageRequest GetAuthorizedStorageRequestAssetBundle(StorageServiceClient client, string resourcePath = "", Dictionary queryParams = null, Dictionary headers = null, int contentLength = 0) { 46 | string requestUrl = RequestUrl(client, queryParams, resourcePath); 47 | StorageRequest request = new StorageRequest(UnityWebRequest.GetAssetBundle(requestUrl)); 48 | request.AuthorizeRequest(client, Method.GET, resourcePath, queryParams, headers, contentLength); 49 | return request; 50 | } 51 | 52 | private static string RequestUrl(StorageServiceClient client, Dictionary queryParams = null, string resourcePath = "") { 53 | string baseUrl = client.PrimaryEndpoint(); 54 | return UrlHelper.BuildQuery(baseUrl, queryParams, resourcePath); 55 | } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /http/auth/headers/AuthorizationHeaders.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using RESTClient; 5 | using System.Text; 6 | using System.Collections.Generic; 7 | using System; 8 | 9 | namespace Azure.StorageServices { 10 | public class AuthorizationHeaders { 11 | private string method; 12 | private CanonicalizedHeaders canonicalizedHeaders; 13 | private string canonicalizedResource; 14 | 15 | private Dictionary authHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase) { 16 | { "Content-Encoding", "" }, 17 | { "Content-Language", "" }, 18 | { "Content-Length", "" }, 19 | { "Content-MD5", "" }, 20 | { "Content-Type", "" }, 21 | { "Date", "" }, 22 | { "If-Modified-Since", "" }, 23 | { "If-Match", "" }, 24 | { "If-None-Match", "" }, 25 | { "If-Unmodified-Since", "" }, 26 | { "Range", "" } 27 | }; 28 | 29 | public AuthorizationHeaders(StorageServiceClient client, Method method, string resourcePath = "", Dictionary queryParams = null, Dictionary headers = null, int contentLength = 0) { 30 | string path = resourcePath; 31 | this.method = method.ToString(); 32 | this.canonicalizedHeaders = new CanonicalizedHeaders(client.Version, headers); 33 | 34 | if (queryParams != null) { 35 | path = resourcePath + BuildQueryString(queryParams); 36 | } 37 | 38 | if (headers != null) { 39 | UpdateHeaderValues(headers); 40 | } 41 | 42 | if (contentLength > 0) { 43 | authHeaders["Content-Length"] = contentLength.ToString(); 44 | } 45 | 46 | // account followed by url encoded resource path, and query params 47 | this.canonicalizedResource = string.Format("/{0}/{1}", client.Account, path); 48 | } 49 | 50 | private string BuildQueryString(Dictionary queryParams) { 51 | StringBuilder q = new StringBuilder(); 52 | foreach (KeyValuePair param in queryParams) { 53 | q.Append("\n" + param.Key + ":" + param.Value); 54 | } 55 | return q.ToString(); 56 | } 57 | 58 | private void UpdateHeaderValues(Dictionary headers) { 59 | foreach (KeyValuePair header in headers) { 60 | if (authHeaders.ContainsKey(header.Key)) { 61 | authHeaders[header.Key] = header.Value; 62 | } 63 | } 64 | } 65 | 66 | public string MSDate() { 67 | return canonicalizedHeaders.MSDate; 68 | } 69 | 70 | public string MSVersion() { 71 | return canonicalizedHeaders.MSVersion; 72 | } 73 | 74 | /// 75 | /// Returns string to sign 76 | /// https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/authentication-for-the-azure-storage-services 77 | /// 78 | public override string ToString() { 79 | StringBuilder sb = new StringBuilder(); 80 | sb.Append(method + "\n"); 81 | foreach (KeyValuePair authHeader in authHeaders) { 82 | sb.Append(authHeader.Value + "\n"); 83 | } 84 | sb.Append(canonicalizedHeaders); 85 | sb.Append(canonicalizedResource); 86 | return sb.ToString(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /http/auth/headers/CanonicalizedHeaders.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace Azure.StorageServices { 9 | public class CanonicalizedHeaders { 10 | private string xMSDate; 11 | 12 | public string MSDate { 13 | get { 14 | return xMSDate; 15 | } 16 | } 17 | 18 | private string xMSVersion; 19 | 20 | public string MSVersion { 21 | get { 22 | return xMSVersion; 23 | } 24 | } 25 | 26 | private List canonicalHeaders; 27 | 28 | public CanonicalizedHeaders(string version, Dictionary headers = null) { 29 | this.xMSDate = DateTime.UtcNow.ToString("R"); 30 | this.xMSVersion = version; 31 | 32 | canonicalHeaders = new List(); 33 | AddCanonicalHeaderKeyValue("x-ms-date", MSDate); 34 | AddCanonicalHeaderKeyValue("x-ms-version", MSVersion); 35 | 36 | if (headers == null) { 37 | return; 38 | } 39 | 40 | foreach (KeyValuePair header in headers) { 41 | if (header.Key.StartsWith("x-ms", StringComparison.OrdinalIgnoreCase)) { 42 | AddCanonicalHeaderKeyValue(header.Key, header.Value); 43 | } 44 | } 45 | } 46 | 47 | private void AddCanonicalHeaderKeyValue(string key, string value) { 48 | canonicalHeaders.Add(FormatKeyValue(key, value)); 49 | } 50 | 51 | private string FormatKeyValue(string key, string value) { 52 | return string.Format("{0}:{1}", key, value); 53 | } 54 | 55 | public override string ToString() { 56 | //return string.Format ("x-ms-date:{0}\nx-ms-version:{1}\n", MSDate, MSVersion); 57 | canonicalHeaders.Sort(); 58 | StringBuilder sb = new StringBuilder(); 59 | foreach (string canonicalHeader in canonicalHeaders) { 60 | sb.Append(canonicalHeader + "\n"); 61 | } 62 | return sb.ToString(); 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /http/query/ResType.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Azure.StorageServices { 5 | public enum ResType { 6 | container, 7 | blob 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /model/BlobResults.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Xml; 6 | using System.Xml.Serialization; 7 | 8 | namespace Azure.StorageServices { 9 | [Serializable] 10 | [XmlRoot("EnumerationResults")] 11 | 12 | public class BlobResults { 13 | [XmlAttribute("ServiceEndpoint")] 14 | public string ServiceEndpoint; 15 | 16 | [XmlAttribute("ContainerName")] 17 | public string ContainerName; 18 | 19 | [XmlArray("Blobs")] 20 | public Blob[] Blobs; 21 | } 22 | 23 | [Serializable] 24 | public class Blob { 25 | public string Name; 26 | 27 | [XmlElement("Properties")] 28 | public BlobProperties Properties; 29 | } 30 | 31 | [Serializable] 32 | public class BlobProperties { 33 | [XmlElement("Last-Modified")] 34 | public string LastModified; 35 | 36 | public string Etag; 37 | 38 | [XmlElement("Content-Length")] 39 | public string ContentLength; 40 | 41 | [XmlElement("Content-Type")] 42 | public string ContentType; 43 | 44 | [XmlElement("Content-Encoding")] 45 | public string ContentEncoding; 46 | 47 | [XmlElement("Content-Language")] 48 | public string ContentLanguage; 49 | 50 | [XmlElement("Content-MD5")] 51 | public string ContentMD5; 52 | 53 | [XmlElement("Cache-Control")] 54 | public string CacheControl; 55 | 56 | [XmlElement("Content-Disposition")] 57 | public string ContentDisposition; 58 | 59 | public string BlobType; 60 | public string LeaseStatus; 61 | public string LeaseState; 62 | public bool ServerEncrypted; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /model/ContainerResults.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Xml; 6 | using System.Xml.Serialization; 7 | 8 | namespace Azure.StorageServices { 9 | [Serializable] 10 | [XmlRoot("EnumerationResults")] 11 | public class ContainerResults { 12 | [XmlAttribute("ServiceEndpoint")] 13 | public string ServiceEndpoint; 14 | 15 | [XmlArray("Containers")] 16 | public Container[] Containers; 17 | } 18 | 19 | [Serializable] 20 | public class Container { 21 | public string Name; 22 | 23 | [XmlElement("Properties")] 24 | public ContainerProperties Properties; 25 | } 26 | 27 | [Serializable] 28 | public class ContainerProperties { 29 | [XmlElement("Last-Modified")] 30 | public string LastModified; 31 | 32 | public string Etag; 33 | public string LeaseStatus; 34 | public string LeaseState; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /model/ErrorResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Xml; 6 | using System.Xml.Serialization; 7 | 8 | namespace Azure.StorageServices { 9 | [Serializable] 10 | [XmlRoot("Error")] 11 | public class ErrorResult { 12 | public string Code; 13 | public string Message; 14 | public string AuthenticationErrorDetail; 15 | } 16 | } 17 | --------------------------------------------------------------------------------