PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/aspclassiccompiler/AzureStoreAsp/Assets/StorageClient/BlobStorage.cs

#
C# | 924 lines | 479 code | 95 blank | 350 comment | 29 complexity | 5cbf6a87b040ed2d1011229844c8f9a5 MD5 | raw file
Possible License(s): Apache-2.0, AGPL-3.0
  1. // ----------------------------------------------------------------------------------
  2. // Microsoft Developer & Platform Evangelism
  3. //
  4. // Copyright (c) Microsoft Corporation. All rights reserved.
  5. //
  6. // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
  7. // EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
  8. // OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
  9. // ----------------------------------------------------------------------------------
  10. // The example companies, organizations, products, domain names,
  11. // e-mail addresses, logos, people, places, and events depicted
  12. // herein are fictitious. No association with any real company,
  13. // organization, product, domain name, email address, logo, person,
  14. // places, or events is intended or should be inferred.
  15. // ----------------------------------------------------------------------------------
  16. //
  17. // <copyright file="BlobStorage.cs" company="Microsoft">
  18. // Copyright (c) Microsoft Corporation. All rights reserved.
  19. // </copyright>
  20. //
  21. using System;
  22. using System.Collections.Generic;
  23. using System.IO;
  24. using System.Collections.Specialized;
  25. using System.Threading;
  26. using System.Diagnostics.CodeAnalysis;
  27. using System.Globalization;
  28. using System.Diagnostics;
  29. [assembly:CLSCompliant(true)]
  30. // disable the generation of warnings for missing documentation elements for
  31. // public classes/members in this file
  32. #pragma warning disable 1591
  33. namespace Microsoft.Samples.ServiceHosting.StorageClient
  34. {
  35. /// <summary>
  36. /// This delegate define the shape of a retry policy. A retry policy will invoke the given
  37. /// <paramref name="action"/> as many times as it wants to in the face of
  38. /// retriable StorageServerExceptions.
  39. /// </summary>
  40. /// <param name="action">The action to retry</param>
  41. /// <returns></returns>
  42. public delegate void RetryPolicy(Action action);
  43. #region Blob Storage API
  44. /// <summary>
  45. /// The entry point of the blob storage API
  46. /// </summary>
  47. public abstract class BlobStorage
  48. {
  49. /// <summary>
  50. /// Factory method for BlobStorage
  51. /// </summary>
  52. /// <param name="baseUri">The base URI of the blob storage service</param>
  53. /// <param name="usePathStyleUris">If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used.
  54. /// If false host-style URIs (http://accountname.baseuri/containername/objectname) are used,
  55. /// where baseuri is the URI of the service.
  56. /// If null, the choice is made automatically: path-style URIs if host name part of base URI is an
  57. /// IP addres, host-style otherwise.</param>
  58. /// <param name="accountName">The name of the storage account</param>
  59. /// <param name="base64Key">Authentication key used for signing requests</param>
  60. /// <returns>A newly created BlobStorage instance</returns>
  61. public static BlobStorage Create(
  62. Uri baseUri,
  63. bool? usePathStyleUris,
  64. string accountName,
  65. string base64Key
  66. )
  67. {
  68. //We create a StorageAccountInfo and then extract the properties of that object.
  69. //This is because the constructor of StorageAccountInfo does normalization of BaseUri.
  70. StorageAccountInfo accountInfo = new StorageAccountInfo(
  71. baseUri,
  72. usePathStyleUris,
  73. accountName,
  74. base64Key
  75. );
  76. return new BlobStorageRest(
  77. accountInfo.BaseUri,
  78. accountInfo.UsePathStyleUris,
  79. accountInfo.AccountName,
  80. accountInfo.Base64Key
  81. );
  82. }
  83. /// <summary>
  84. /// Factory method for BlobStorage
  85. /// </summary>
  86. /// <param name="accountInfo">Account information</param>
  87. /// <returns>A newly created BlobStorage instance</returns>
  88. public static BlobStorage Create(StorageAccountInfo accountInfo)
  89. {
  90. return new BlobStorageRest(
  91. accountInfo.BaseUri,
  92. accountInfo.UsePathStyleUris,
  93. accountInfo.AccountName,
  94. accountInfo.Base64Key
  95. );
  96. }
  97. /// <summary>
  98. /// Get a reference to a newly created BlobContainer object.
  99. /// This method does not make any calls to the storage service.
  100. /// </summary>
  101. /// <param name="containerName">The name of the container</param>
  102. /// <returns>A reference to a newly created BlobContainer object</returns>
  103. public abstract BlobContainer GetBlobContainer(string containerName);
  104. /// <summary>
  105. /// Lists the containers within the account.
  106. /// </summary>
  107. /// <returns>A list of containers</returns>
  108. public abstract IEnumerable<BlobContainer> ListBlobContainers();
  109. /// <summary>
  110. /// The time out for each request to the storage service.
  111. /// </summary>
  112. public TimeSpan Timeout
  113. {
  114. get;
  115. set;
  116. }
  117. /// <summary>
  118. /// The retry policy used for retrying requests
  119. /// </summary>
  120. public RetryPolicy RetryPolicy
  121. {
  122. get;
  123. set;
  124. }
  125. /// <summary>
  126. /// The base URI of the blob storage service
  127. /// </summary>
  128. public Uri BaseUri
  129. {
  130. get
  131. {
  132. return this.baseUri;
  133. }
  134. }
  135. /// <summary>
  136. /// The name of the storage account
  137. /// </summary>
  138. public string AccountName
  139. {
  140. get
  141. {
  142. return this.accountName;
  143. }
  144. }
  145. /// <summary>
  146. /// Indicates whether to use/generate path-style or host-style URIs
  147. /// </summary>
  148. public bool UsePathStyleUris
  149. {
  150. get
  151. {
  152. return this.usePathStyleUris;
  153. }
  154. }
  155. /// <summary>
  156. /// The default timeout
  157. /// </summary>
  158. [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
  159. Justification = "TimeSpan is a non-mutable type")]
  160. public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);
  161. /// <summary>
  162. /// The default retry policy
  163. /// </summary>
  164. [SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
  165. Justification = "RetryPolicy is a non-mutable type")]
  166. public static readonly RetryPolicy DefaultRetryPolicy = RetryPolicies.NoRetry;
  167. internal protected BlobStorage(Uri baseUri,
  168. bool? usePathStyleUris,
  169. string accountName,
  170. string base64Key
  171. )
  172. {
  173. this.baseUri = baseUri;
  174. this.accountName = accountName;
  175. this.Base64Key = base64Key;
  176. if (usePathStyleUris == null)
  177. this.usePathStyleUris = Utilities.StringIsIPAddress(baseUri.Host);
  178. else
  179. this.usePathStyleUris = usePathStyleUris.Value;
  180. Timeout = DefaultTimeout;
  181. RetryPolicy = DefaultRetryPolicy;
  182. }
  183. private bool usePathStyleUris;
  184. private Uri baseUri;
  185. private string accountName;
  186. protected internal string Base64Key
  187. {
  188. get;
  189. set;
  190. }
  191. }
  192. /// <summary>
  193. /// Provides definitions for some standard retry policies.
  194. /// </summary>
  195. public static class RetryPolicies
  196. {
  197. public static readonly TimeSpan StandardMinBackoff = TimeSpan.FromMilliseconds(100);
  198. public static readonly TimeSpan StandardMaxBackoff = TimeSpan.FromSeconds(30);
  199. private static readonly Random random = new Random();
  200. /// <summary>
  201. /// Policy that does no retries i.e., it just invokes <paramref name="action"/> exactly once
  202. /// </summary>
  203. /// <param name="action">The action to retry</param>
  204. /// <returns>The return value of <paramref name="action"/></returns>
  205. public static void NoRetry(Action action)
  206. {
  207. try
  208. {
  209. action();
  210. }
  211. catch (TableRetryWrapperException e)
  212. {
  213. throw e.InnerException;
  214. }
  215. }
  216. /// <summary>
  217. /// Policy that retries a specified number of times with a specified fixed time interval between retries
  218. /// </summary>
  219. /// <param name="numberOfRetries">The number of times to retry. Should be a non-negative number</param>
  220. /// <param name="intervalBetweenRetries">The time interval between retries. Use TimeSpan.Zero to specify immediate
  221. /// retries</param>
  222. /// <returns></returns>
  223. /// <remarks>When <paramref name="numberOfRetries"/> is 0 and <paramref name="intervalBetweenRetries"/> is
  224. /// TimeSpan.Zero this policy is equivalent to the NoRetry policy</remarks>
  225. public static RetryPolicy RetryN(int numberOfRetries, TimeSpan intervalBetweenRetries)
  226. {
  227. return new RetryPolicy((Action action) =>
  228. {
  229. RetryNImpl(action, numberOfRetries, intervalBetweenRetries);
  230. }
  231. );
  232. }
  233. /// <summary>
  234. /// Policy that retries a specified number of times with a randomized exponential backoff scheme
  235. /// </summary>
  236. /// <param name="numberOfRetries">The number of times to retry. Should be a non-negative number.</param>
  237. /// <param name="deltaBackoff">The multiplier in the exponential backoff scheme</param>
  238. /// <returns></returns>
  239. /// <remarks>For this retry policy, the minimum amount of milliseconds between retries is given by the
  240. /// StandardMinBackoff constant, and the maximum backoff is predefined by the StandardMaxBackoff constant.
  241. /// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff.</remarks>
  242. public static RetryPolicy RetryExponentialN(int numberOfRetries, TimeSpan deltaBackoff)
  243. {
  244. return new RetryPolicy((Action action) =>
  245. {
  246. RetryExponentialNImpl(action, numberOfRetries, StandardMinBackoff, StandardMaxBackoff, deltaBackoff);
  247. }
  248. );
  249. }
  250. /// <summary>
  251. /// Policy that retries a specified number of times with a randomized exponential backoff scheme
  252. /// </summary>
  253. /// <param name="numberOfRetries">The number of times to retry. Should be a non-negative number</param>
  254. /// <param name="deltaBackoff">The multiplier in the exponential backoff scheme</param>
  255. /// <param name="minBackoff">The minimum backoff interval</param>
  256. /// <param name="maxBackoff">The maximum backoff interval</param>
  257. /// <returns></returns>
  258. /// <remarks>For this retry policy, the minimum amount of milliseconds between retries is given by the
  259. /// minBackoff parameter, and the maximum backoff is predefined by the maxBackoff parameter.
  260. /// Otherwise, the backoff is calculated as random(2^currentRetry) * deltaBackoff.</remarks>
  261. public static RetryPolicy RetryExponentialN(int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff)
  262. {
  263. if (minBackoff > maxBackoff)
  264. {
  265. throw new ArgumentException("The minimum backoff must not be larger than the maximum backoff period.");
  266. }
  267. if (minBackoff < TimeSpan.Zero)
  268. {
  269. throw new ArgumentException("The minimum backoff period must not be negative.");
  270. }
  271. return new RetryPolicy((Action action) =>
  272. {
  273. RetryExponentialNImpl(action, numberOfRetries, minBackoff, maxBackoff, deltaBackoff);
  274. }
  275. );
  276. }
  277. #region private helper methods
  278. private static void RetryNImpl(Action action, int numberOfRetries, TimeSpan intervalBetweenRetries)
  279. {
  280. do
  281. {
  282. try
  283. {
  284. action();
  285. break;
  286. }
  287. catch (StorageServerException)
  288. {
  289. if (numberOfRetries == 0)
  290. {
  291. throw;
  292. }
  293. if (intervalBetweenRetries > TimeSpan.Zero)
  294. {
  295. Thread.Sleep(intervalBetweenRetries);
  296. }
  297. }
  298. catch (TableRetryWrapperException e)
  299. {
  300. if (numberOfRetries == 0)
  301. {
  302. throw e.InnerException;
  303. }
  304. if (intervalBetweenRetries > TimeSpan.Zero)
  305. {
  306. Thread.Sleep(intervalBetweenRetries);
  307. }
  308. }
  309. }
  310. while (numberOfRetries-- > 0);
  311. }
  312. private static void RetryExponentialNImpl(Action action, int numberOfRetries, TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff)
  313. {
  314. int totalNumberOfRetries = numberOfRetries;
  315. TimeSpan backoff;
  316. // sanity check
  317. // this is already checked when creating the retry policy in case other than the standard settings are used
  318. // because this library is available in source code, the standard settings can be changed and thus we
  319. // check again at this point
  320. if (minBackoff > maxBackoff)
  321. {
  322. throw new ArgumentException("The minimum backoff must not be larger than the maximum backoff period.");
  323. }
  324. if (minBackoff < TimeSpan.Zero)
  325. {
  326. throw new ArgumentException("The minimum backoff period must not be negative.");
  327. }
  328. do
  329. {
  330. try
  331. {
  332. action();
  333. break;
  334. }
  335. catch (StorageServerException)
  336. {
  337. if (numberOfRetries == 0)
  338. {
  339. throw;
  340. }
  341. backoff = CalculateCurrentBackoff(minBackoff, maxBackoff, deltaBackoff, totalNumberOfRetries - numberOfRetries);
  342. Debug.Assert(backoff >= minBackoff);
  343. Debug.Assert(backoff <= maxBackoff);
  344. if (backoff > TimeSpan.Zero) {
  345. Thread.Sleep(backoff);
  346. }
  347. }
  348. catch (TableRetryWrapperException e)
  349. {
  350. if (numberOfRetries == 0)
  351. {
  352. throw e.InnerException;
  353. }
  354. backoff = CalculateCurrentBackoff(minBackoff, maxBackoff, deltaBackoff, totalNumberOfRetries - numberOfRetries);
  355. Debug.Assert(backoff >= minBackoff);
  356. Debug.Assert(backoff <= maxBackoff);
  357. if (backoff > TimeSpan.Zero)
  358. {
  359. Thread.Sleep(backoff);
  360. }
  361. }
  362. }
  363. while (numberOfRetries-- > 0);
  364. }
  365. private static TimeSpan CalculateCurrentBackoff(TimeSpan minBackoff, TimeSpan maxBackoff, TimeSpan deltaBackoff, int curRetry)
  366. {
  367. long backoff;
  368. if (curRetry > 30)
  369. {
  370. backoff = maxBackoff.Ticks;
  371. }
  372. else
  373. {
  374. try
  375. {
  376. checked
  377. {
  378. // only randomize the multiplier here
  379. // it would be as correct to randomize the whole backoff result
  380. lock (random)
  381. {
  382. backoff = random.Next((1 << curRetry) + 1);
  383. }
  384. // Console.WriteLine("backoff:" + backoff);
  385. // Console.WriteLine("random range: [0, " + ((1 << curRetry) + 1) + "]");
  386. backoff *= deltaBackoff.Ticks;
  387. backoff += minBackoff.Ticks;
  388. }
  389. }
  390. catch (OverflowException)
  391. {
  392. backoff = maxBackoff.Ticks;
  393. }
  394. if (backoff > maxBackoff.Ticks)
  395. {
  396. backoff = maxBackoff.Ticks;
  397. }
  398. }
  399. Debug.Assert(backoff >= minBackoff.Ticks);
  400. Debug.Assert(backoff <= maxBackoff.Ticks);
  401. return TimeSpan.FromTicks(backoff);
  402. }
  403. #endregion
  404. }
  405. /// <summary>
  406. /// Access control for containers
  407. /// </summary>
  408. public enum ContainerAccessControl
  409. {
  410. Private,
  411. Public
  412. }
  413. /// <summary>
  414. /// The blob container class.
  415. /// Used to access and enumerate blobs in the container.
  416. /// Storage key credentials are needed to access private blobs but not for public blobs.
  417. /// </summary>
  418. public abstract class BlobContainer
  419. {
  420. /// <summary>
  421. /// Use this constructor to access private blobs.
  422. /// </summary>
  423. /// <param name="baseUri">The base Uri for the storage endpoint</param>
  424. /// <param name="accountName">Name of the storage account</param>
  425. /// <param name="containerName">Name of the container</param>
  426. internal protected BlobContainer(Uri baseUri, string accountName, string containerName)
  427. : this(baseUri, true, accountName, containerName, DateTime.MinValue)
  428. {}
  429. /// <summary>
  430. /// Use this constructor to access private blobs.
  431. /// </summary>
  432. /// <param name="baseUri">The base Uri for the storage endpoint</param>
  433. /// <param name="usePathStyleUris">
  434. /// If true, path-style URIs (http://baseuri/accountname/containername/objectname) are used and if false
  435. /// host-style URIs (http://accountname.baseuri/containername/objectname) are used, where baseuri is the
  436. /// URI of the service
  437. /// </param>
  438. /// <param name="accountName">Name of the storage account</param>
  439. /// <param name="containerName">Name of the container</param>
  440. /// <param name="lastModified">Date of last modification</param>
  441. internal protected BlobContainer(Uri baseUri, bool usePathStyleUris, string accountName, string containerName, DateTime lastModified)
  442. {
  443. if (!Utilities.IsValidContainerOrQueueName(containerName))
  444. {
  445. throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "The specified container name \"{0}\" is not valid!" +
  446. "Please choose a name that conforms to the naming conventions for containers!", containerName));
  447. }
  448. this.baseUri = baseUri;
  449. this.usePathStyleUris = usePathStyleUris;
  450. this.accountName = accountName;
  451. this.containerName = containerName;
  452. this.Timeout = BlobStorage.DefaultTimeout;
  453. this.RetryPolicy = BlobStorage.DefaultRetryPolicy;
  454. this.LastModifiedTime = lastModified;
  455. }
  456. /// <summary>
  457. /// The time out for each request to the storage service.
  458. /// </summary>
  459. public TimeSpan Timeout
  460. {
  461. get;
  462. set;
  463. }
  464. /// <summary>
  465. /// The retry policy used for retrying requests
  466. /// </summary>
  467. public RetryPolicy RetryPolicy
  468. {
  469. get;
  470. set;
  471. }
  472. /// <summary>
  473. /// The base URI of the blob storage service
  474. /// </summary>
  475. public Uri BaseUri
  476. {
  477. get
  478. {
  479. return this.baseUri;
  480. }
  481. }
  482. /// <summary>
  483. /// The name of the storage account
  484. /// </summary>
  485. public string AccountName
  486. {
  487. get
  488. {
  489. return this.accountName;
  490. }
  491. }
  492. /// <summary>
  493. /// The name of the blob container.
  494. /// </summary>
  495. public string ContainerName
  496. {
  497. get
  498. {
  499. return this.containerName;
  500. }
  501. }
  502. /// <summary>
  503. /// Indicates whether to use/generate path-style or host-style URIs
  504. /// </summary>
  505. public bool UsePathStyleUris
  506. {
  507. get
  508. {
  509. return this.usePathStyleUris;
  510. }
  511. }
  512. /// <summary>
  513. /// The URI of the container
  514. /// </summary>
  515. public abstract Uri ContainerUri
  516. {
  517. get;
  518. }
  519. /// <summary>
  520. /// The timestamp for last modification of container.
  521. /// </summary>
  522. public DateTime LastModifiedTime
  523. {
  524. get;
  525. protected set;
  526. }
  527. /// <summary>
  528. /// Create the container if it does not exist.
  529. /// The container is created with private access control and no metadata.
  530. /// </summary>
  531. /// <returns>true if the container was created. false if the container already exists</returns>
  532. public abstract bool CreateContainer();
  533. /// <summary>
  534. /// Create the container with the specified metadata and access control if it does not exist
  535. /// </summary>
  536. /// <param name="metadata">The metadata for the container. Can be null to indicate no metadata</param>
  537. /// <param name="accessControl">The access control (public or private) with which to create the container</param>
  538. /// <returns>true if the container was created. false if the container already exists</returns>
  539. public abstract bool CreateContainer(NameValueCollection metadata, ContainerAccessControl accessControl);
  540. /// <summary>
  541. /// Check if the blob container exists
  542. /// </summary>
  543. /// <returns>true if the container exists, false otherwise.</returns>
  544. public abstract bool DoesContainerExist();
  545. /// <summary>
  546. /// Get the properties for the container if it exists.
  547. /// </summary>
  548. /// <returns>The properties for the container if it exists, null otherwise</returns>
  549. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
  550. Justification="The method makes a call to the blob service")]
  551. public abstract ContainerProperties GetContainerProperties();
  552. /// <summary>
  553. /// Get the access control permissions associated with the container.
  554. /// </summary>
  555. /// <returns></returns>
  556. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
  557. Justification = "The method makes a call to the blob service")]
  558. public abstract ContainerAccessControl GetContainerAccessControl();
  559. /// <summary>
  560. /// Set the access control permissions associated with the container.
  561. /// </summary>
  562. /// <param name="acl">The permission to set</param>
  563. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
  564. Justification = "The method makes a call to the blob service")]
  565. public abstract void SetContainerAccessControl(ContainerAccessControl acl);
  566. /// <summary>
  567. /// Deletes the current container.
  568. /// </summary>
  569. public abstract bool DeleteContainer();
  570. /// <summary>
  571. /// Check if the blob container exists
  572. /// </summary>
  573. /// <param name="blobName">Name of the BLOB.</param>
  574. /// <returns>true if the blob exists, false otherwise.</returns>
  575. public abstract bool DoesBlobExist(string blobName);
  576. /// <summary>
  577. /// Create a new blob or overwrite an existing blob.
  578. /// </summary>
  579. /// <param name="blobProperties">The properties of the blob</param>
  580. /// <param name="blobContents">The contents of the blob</param>
  581. /// <param name="overwrite">Should this request overwrite an existing blob ?</param>
  582. /// <returns>true if the blob was created. false if the blob already exists and <paramref name="overwrite"/>was set to false</returns>
  583. /// <remarks>The LastModifiedTime property of <paramref name="blobProperties"/> is set as a result of this call.
  584. /// This method also has an effect on the ETag values that are managed by the service.</remarks>
  585. public abstract bool CreateBlob(BlobProperties blobProperties, BlobContents blobContents, bool overwrite);
  586. /// <summary>
  587. /// Updates an existing blob if it has not been modified since the specified time which is typically
  588. /// the last modified time of the blob when you retrieved it.
  589. /// Use this method to implement optimistic concurrency by avoiding clobbering changes to the blob
  590. /// made by another writer.
  591. /// </summary>
  592. /// <param name="blob">The properties of the blob. This object should be one previously
  593. /// obtained from a call to GetBlob or GetBlobProperties and have its LastModifiedTime property set.</param>
  594. /// <param name="contents">The contents of the blob. The contents of the blob should be readable</param>
  595. /// <returns>true if the blob was updated. false if the blob has changed since the last time</returns>
  596. /// <remarks>The LastModifiedTime property of <paramref name="blob"/> is set as a result of this call.
  597. /// This method also has an effect on the ETag values that are managed by the service if the update was
  598. /// successful.</remarks>
  599. public abstract bool UpdateBlobIfNotModified(BlobProperties blob, BlobContents contents);
  600. /// <summary>
  601. /// Get the blob contents and properties if the blob exists
  602. /// </summary>
  603. /// <param name="name">The name of the blob</param>
  604. /// <param name="blobContents">Object in which the contents are returned.
  605. /// This object should contain a writable stream or should be a default constructed object.</param>
  606. /// <param name="transferAsChunks">Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure.</param>
  607. /// <returns>The properties of the blob if the blob exists.</returns>
  608. public abstract BlobProperties GetBlob(string name, BlobContents blobContents, bool transferAsChunks);
  609. /// <summary>
  610. /// Gets the blob contents and properties if the blob has not been modified since the time specified.
  611. /// Use this method if you have cached the contents of a blob and want to avoid retrieving the blob
  612. /// if it has not changed since the last time you retrieved it.
  613. /// </summary>
  614. /// <param name="blobProperties">The properties of the blob obtained from an earlier call to GetBlob. This
  615. /// parameter is updated by the call if the blob has been modified</param>
  616. /// <param name="blobContents">Contains the stream to which the contents of the blob are written if it has been
  617. /// modified</param>
  618. /// <param name="transferAsChunks">Should the blob be gotten in pieces. This requires more round-trips, but will retry smaller pieces in case of failure.</param>
  619. /// <returns>true if the blob has been modified, false otherwise</returns>
  620. public abstract bool GetBlobIfModified(BlobProperties blobProperties, BlobContents blobContents, bool transferAsChunks);
  621. /// <summary>
  622. /// Get the properties of the blob if it exists.
  623. /// This method is also the simplest way to check if a blob exists.
  624. /// </summary>
  625. /// <param name="name">The name of the blob</param>
  626. /// <returns>The properties of the blob if it exists. null otherwise.
  627. /// The properties for the contents of the blob are not set</returns>
  628. public abstract BlobProperties GetBlobProperties(string name);
  629. /// <summary>
  630. /// Set the metadata of an existing blob.
  631. /// </summary>
  632. /// <param name="blobProperties">The blob properties object whose metadata is to be updated</param>
  633. public abstract void UpdateBlobMetadata(BlobProperties blobProperties);
  634. /// <summary>
  635. /// Set the metadata of an existing blob if it has not been modified since it was last retrieved.
  636. /// </summary>
  637. /// <param name="blobProperties">The blob properties object whose metadata is to be updated.
  638. /// Typically obtained by a previous call to GetBlob or GetBlobProperties</param>
  639. /// <returns>true if the blob metadata was updated. false if it was not updated because the blob
  640. /// has been modified</returns>
  641. public abstract bool UpdateBlobMetadataIfNotModified(BlobProperties blobProperties);
  642. /// <summary>
  643. /// Delete a blob with the given name
  644. /// </summary>
  645. /// <param name="name">The name of the blob</param>
  646. /// <returns>true if the blob exists and was successfully deleted, false if the blob does not exist</returns>
  647. public abstract bool DeleteBlob(string name);
  648. /// <summary>
  649. /// Delete a blob with the given name if the blob has not been modified since it was last obtained.
  650. /// Use this method for optimistic concurrency to avoid deleting a blob that has been modified since
  651. /// the last time you retrieved it
  652. /// </summary>
  653. /// <param name="blob">A blob object (typically previously obtained from a GetBlob call)</param>
  654. /// <param name="modified">This out parameter is set to true if the blob was not deleted because
  655. /// it was modified</param>
  656. /// <returns>true if the blob exists and was successfully deleted, false if the blob does not exist or was
  657. /// not deleted because the blob was modified.</returns>
  658. public abstract bool DeleteBlobIfNotModified(BlobProperties blob, out bool modified);
  659. /// <summary>
  660. /// Enumerates all blobs with a given prefix.
  661. /// </summary>
  662. /// <param name="prefix"></param>
  663. /// <param name="combineCommonPrefixes">If true common prefixes with "/" as seperator</param>
  664. /// <returns>The list of blob properties and common prefixes</returns>
  665. public abstract IEnumerable<object> ListBlobs(string prefix, bool combineCommonPrefixes);
  666. private Uri baseUri;
  667. private string accountName;
  668. private string containerName;
  669. private bool usePathStyleUris;
  670. }
  671. /// <summary>
  672. /// The properties of a blob.
  673. /// No member of this class makes a storage service request.
  674. /// </summary>
  675. public class BlobProperties
  676. {
  677. /// <summary>
  678. /// Construct a new BlobProperties object
  679. /// </summary>
  680. /// <param name="name">The name of the blob</param>
  681. public BlobProperties(string name)
  682. {
  683. Name = name;
  684. }
  685. /// <summary>
  686. /// Name of the blob
  687. /// </summary>
  688. public string Name { get; internal set; }
  689. /// <summary>
  690. /// URI of the blob
  691. /// </summary>
  692. public Uri Uri { get; internal set; }
  693. /// <summary>
  694. /// Content encoding of the blob if it set, null otherwise.
  695. /// </summary>
  696. public string ContentEncoding { get; set; }
  697. /// <summary>
  698. /// Content Type of the blob if it is set, null otherwise.
  699. /// </summary>
  700. public string ContentType { get; set; }
  701. /// <summary>
  702. /// Content Language of the blob if it is set, null otherwise.
  703. /// </summary>
  704. public string ContentLanguage { get; set; }
  705. /// <summary>
  706. /// The length of the blob content, null otherwise.
  707. /// </summary>
  708. public long ContentLength { get; internal set; }
  709. /// <summary>
  710. /// Metadata for the blob in the form of name-value pairs.
  711. /// </summary>
  712. public NameValueCollection Metadata { get; set;}
  713. /// <summary>
  714. /// The last modified time for the blob.
  715. /// </summary>
  716. public DateTime LastModifiedTime { get; internal set; }
  717. /// <summary>
  718. /// The ETag of the blob. This is an identifier assigned to the blob by the storage service
  719. /// and is used to distinguish contents of two blobs (or versions of the same blob).
  720. /// </summary>
  721. public string ETag { get; internal set; }
  722. internal void Assign(BlobProperties other)
  723. {
  724. Name = other.Name;
  725. Uri = other.Uri;
  726. ContentEncoding = other.ContentEncoding;
  727. ContentLanguage = other.ContentLanguage;
  728. ContentLength = other.ContentLength;
  729. ContentType = other.ContentType;
  730. ETag = other.ETag;
  731. LastModifiedTime = other.LastModifiedTime;
  732. Metadata = (other.Metadata != null ? new NameValueCollection(other.Metadata) : null) ;
  733. }
  734. }
  735. /// <summary>
  736. /// The properties of a container.
  737. /// No member of this class makes a storage service request.
  738. /// </summary>
  739. public class ContainerProperties
  740. {
  741. public ContainerProperties(string name)
  742. {
  743. Name = name;
  744. }
  745. public string Name
  746. {
  747. get;
  748. internal set;
  749. }
  750. public DateTime LastModifiedTime
  751. {
  752. get;
  753. internal set;
  754. }
  755. public string ETag
  756. {
  757. get;
  758. internal set;
  759. }
  760. public Uri Uri
  761. {
  762. get;
  763. internal set;
  764. }
  765. public NameValueCollection Metadata
  766. {
  767. get;
  768. internal set;
  769. }
  770. }
  771. /// <summary>
  772. /// The contents of the Blob in various forms.
  773. /// </summary>
  774. public class BlobContents
  775. {
  776. /// <summary>
  777. /// Construct a new BlobContents object from a stream.
  778. /// </summary>
  779. /// <param name="stream">The stream to/from which blob contents are written/read. The
  780. /// stream should be seekable in order for requests to be retried.</param>
  781. public BlobContents(Stream stream)
  782. {
  783. this.stream = stream;
  784. }
  785. /// <summary>
  786. /// Construct a new BlobContents object from a byte array.
  787. /// </summary>
  788. /// <param name="value">The byte array to/from which contents are written/read.</param>
  789. public BlobContents(byte[] value)
  790. {
  791. this.bytes = value;
  792. this.stream = new MemoryStream(value, false);
  793. }
  794. /// <summary>
  795. /// Get the contents of a blob as a stream.
  796. /// </summary>
  797. public Stream AsStream
  798. {
  799. get
  800. {
  801. return stream;
  802. }
  803. }
  804. /// <summary>
  805. /// Get the contents of a blob as a byte array.
  806. /// </summary>
  807. public byte[] AsBytes()
  808. {
  809. if (bytes != null)
  810. return bytes;
  811. if (stream != null)
  812. {
  813. stream.Seek(0, SeekOrigin.Begin);
  814. bytes = new byte[stream.Length];
  815. int n = 0;
  816. int offset = 0;
  817. do
  818. {
  819. n = stream.Read(bytes, offset, bytes.Length - offset);
  820. offset += n;
  821. } while (n > 0);
  822. }
  823. return bytes;
  824. }
  825. private Stream stream;
  826. private byte[] bytes;
  827. }
  828. #endregion
  829. }