Convert all url usages to use the Uri class instead of raw strings.
[pithos-ms-client] / trunk / Pithos.Network / docs / cloudfilesclient.html
1 <!DOCTYPE html />
2
3 <html>
4 <head>
5         <title>CloudFilesClient.cs</title>
6         <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
7         <link href="focco.css" rel="stylesheet" media="all" type="text/css" />
8         <script src="prettify.js" type="text/javascript"></script>
9 </head>
10 <body onload="prettyPrint()">
11         <div id="container">
12                 <div id="background"></div>
13                 <table cellpadding="0" cellspacing="0">
14                         <thead>
15                                 <tr>
16                                         <th class="docs">
17                                                 <h1>CloudFilesClient.cs</h1>
18                                         </th>
19                                         <th class="code"></th>
20                                 </tr>
21                         </thead>
22                         <tbody>
23                                         <tr id="section_1">
24                                                 <td class="docs">
25                                                         <div class="pilwrap">
26                                                                 <a class="pilcrow" href="#section_1">&#182;</a>
27                                                         </div>
28                                                         <p><strong>CloudFilesClient</strong> provides a simple client interface to CloudFiles and Pithos</p>
29
30 <p>The class provides methods to upload/download files, delete files, manage containers</p>
31
32                                                 </td>
33                                                 <td class="code">
34                                                         <pre><code class='prettyprint'>
35
36 using System;
37 using System.Collections.Generic;
38 using System.ComponentModel.Composition;
39 using System.Diagnostics;
40 using System.Diagnostics.Contracts;
41 using System.Globalization;
42 using System.IO;
43 using System.Linq;
44 using System.Net;
45 using System.Security.Cryptography;
46 using System.Text;
47 using System.Threading.Algorithms;
48 using System.Threading.Tasks;
49 using Newtonsoft.Json;
50 using Pithos.Interfaces;
51 using WebHeaderCollection = System.Net.WebHeaderCollection;
52
53 namespace Pithos.Network
54 {
55     [Export(typeof(ICloudClient))]
56     public class CloudFilesClient:ICloudClient
57     {
58 </code></pre>
59                                                 </td>
60                                         </tr>
61                                         <tr id="section_2">
62                                                 <td class="docs">
63                                                         <div class="pilwrap">
64                                                                 <a class="pilcrow" href="#section_2">&#182;</a>
65                                                         </div>
66                                                         <p>CloudFilesClient uses <em>_baseClient</em> internally to communicate with the server
67 RestClient provides a REST-friendly interface over the standard WebClient.</p>
68
69                                                 </td>
70                                                 <td class="code">
71                                                         <pre><code class='prettyprint'>        private RestClient _baseClient;
72         
73 </code></pre>
74                                                 </td>
75                                         </tr>
76                                         <tr id="section_3">
77                                                 <td class="docs">
78                                                         <div class="pilwrap">
79                                                                 <a class="pilcrow" href="#section_3">&#182;</a>
80                                                         </div>
81                                                         <p>Some operations can specify a Timeout. The default value of all timeouts is 10 seconds</p>
82
83                                                 </td>
84                                                 <td class="code">
85                                                         <pre><code class='prettyprint'>        private readonly TimeSpan _shortTimeout = TimeSpan.FromSeconds(10);
86         
87 </code></pre>
88                                                 </td>
89                                         </tr>
90                                         <tr id="section_4">
91                                                 <td class="docs">
92                                                         <div class="pilwrap">
93                                                                 <a class="pilcrow" href="#section_4">&#182;</a>
94                                                         </div>
95                                                         <p>Some operations can be retried before failing. The default number of retries is 5</p>
96
97                                                 </td>
98                                                 <td class="code">
99                                                         <pre><code class='prettyprint'>        private readonly int _retries = 5;        
100         
101 </code></pre>
102                                                 </td>
103                                         </tr>
104                                         <tr id="section_5">
105                                                 <td class="docs">
106                                                         <div class="pilwrap">
107                                                                 <a class="pilcrow" href="#section_5">&#182;</a>
108                                                         </div>
109                                                         <p>During authentication the client provides a UserName </p>
110
111                                                 </td>
112                                                 <td class="code">
113                                                         <pre><code class='prettyprint'>        public string UserName { get; set; }
114         
115 </code></pre>
116                                                 </td>
117                                         </tr>
118                                         <tr id="section_6">
119                                                 <td class="docs">
120                                                         <div class="pilwrap">
121                                                                 <a class="pilcrow" href="#section_6">&#182;</a>
122                                                         </div>
123                                                         <p>and and ApiKey to the server</p>
124
125                                                 </td>
126                                                 <td class="code">
127                                                         <pre><code class='prettyprint'>        public string ApiKey { get; set; }
128         
129 </code></pre>
130                                                 </td>
131                                         </tr>
132                                         <tr id="section_7">
133                                                 <td class="docs">
134                                                         <div class="pilwrap">
135                                                                 <a class="pilcrow" href="#section_7">&#182;</a>
136                                                         </div>
137                                                         <p>And receives an authentication Token. This token must be provided in ALL other operations,
138 in the X-Auth-Token header</p>
139
140                                                 </td>
141                                                 <td class="code">
142                                                         <pre><code class='prettyprint'>        public string Token { get; set; }
143         
144 </code></pre>
145                                                 </td>
146                                         </tr>
147                                         <tr id="section_8">
148                                                 <td class="docs">
149                                                         <div class="pilwrap">
150                                                                 <a class="pilcrow" href="#section_8">&#182;</a>
151                                                         </div>
152                                                         <p>The client also receives a StorageUrl after authentication. All subsequent operations must
153 use this url</p>
154
155                                                 </td>
156                                                 <td class="code">
157                                                         <pre><code class='prettyprint'>        public Uri StorageUrl { get; set; }
158         
159         public Uri Proxy { get; set; }
160
161         public double DownloadPercentLimit { get; set; }
162         public double UploadPercentLimit { get; set; }
163
164         public string AuthenticationUrl { get; set; }
165
166  
167         public string VersionPath
168         {
169             get { return UsePithos ? &quot;v1&quot; : &quot;v1.0&quot;; }
170         }
171
172         public bool UsePithos { get; set; }
173
174         private bool _authenticated = false;
175
176 </code></pre>
177                                                 </td>
178                                         </tr>
179                                         <tr id="section_9">
180                                                 <td class="docs">
181                                                         <div class="pilwrap">
182                                                                 <a class="pilcrow" href="#section_9">&#182;</a>
183                                                         </div>
184                                                         <p></p>
185
186                                                 </td>
187                                                 <td class="code">
188                                                         <pre><code class='prettyprint'>        public void Authenticate(string userName,string apiKey)
189         {
190             Trace.TraceInformation(&quot;[AUTHENTICATE] Start for {0}&quot;, userName);
191             if (String.IsNullOrWhiteSpace(userName))
192                 throw new ArgumentNullException(&quot;userName&quot;, &quot;The userName property can&#39;t be empty&quot;);
193             if (String.IsNullOrWhiteSpace(apiKey))
194                 throw new ArgumentNullException(&quot;apiKey&quot;, &quot;The apiKey property can&#39;t be empty&quot;);
195
196             if (_authenticated)
197                 return;
198
199             UserName = userName;
200             ApiKey = apiKey;
201             
202
203             using (var authClient = new RestClient{BaseAddress=AuthenticationUrl})
204             {
205                 if (Proxy != null)
206                     authClient.Proxy = new WebProxy(Proxy);
207
208                 authClient.Headers.Add(&quot;X-Auth-User&quot;, UserName);
209                 authClient.Headers.Add(&quot;X-Auth-Key&quot;, ApiKey);
210
211                 authClient.DownloadStringWithRetry(VersionPath, 3);
212
213                 authClient.AssertStatusOK(&quot;Authentication failed&quot;);
214
215                 var storageUrl = authClient.GetHeaderValue(&quot;X-Storage-Url&quot;);
216                 if (String.IsNullOrWhiteSpace(storageUrl))
217                     throw new InvalidOperationException(&quot;Failed to obtain storage url&quot;);
218                 StorageUrl = new Uri(storageUrl);
219                 
220                 var token = authClient.GetHeaderValue(&quot;X-Auth-Token&quot;);
221                 if (String.IsNullOrWhiteSpace(token))
222                     throw new InvalidOperationException(&quot;Failed to obtain token url&quot;);
223                 Token = token;
224             }
225
226             _baseClient = new RestClient{
227                 BaseAddress  = StorageUrl.AbsoluteUri,                
228                 Timeout=10000,
229                 Retries=3};
230             if (Proxy!=null)
231                 _baseClient.Proxy = new WebProxy(Proxy);
232
233             _baseClient.Headers.Add(&quot;X-Auth-Token&quot;, Token);
234
235             Trace.TraceInformation(&quot;[AUTHENTICATE] End for {0}&quot;, userName);
236         }
237
238
239         public IList&lt;ContainerInfo&gt; ListContainers()
240         {
241             using (var client = new RestClient(_baseClient))
242             {
243                 var content = client.DownloadStringWithRetry(&quot;&quot;, 3);
244                 client.Parameters.Clear();
245                 client.Parameters.Add(&quot;format&quot;, &quot;json&quot;);
246                 client.AssertStatusOK(&quot;List Containers failed&quot;);
247
248                 if (client.StatusCode == HttpStatusCode.NoContent)
249                     return new List&lt;ContainerInfo&gt;();
250                 var infos = JsonConvert.DeserializeObject&lt;IList&lt;ContainerInfo&gt;&gt;(content);
251                 return infos;
252             }
253
254         }
255
256         public IList&lt;ObjectInfo&gt; ListObjects(string container, DateTime? since = null)
257         {
258             if (String.IsNullOrWhiteSpace(container))
259                 throw new ArgumentNullException(&quot;container&quot;);
260             Contract.EndContractBlock();
261
262             Trace.TraceInformation(&quot;[START] ListObjects&quot;);
263
264             using (var client = new RestClient(_baseClient))
265             {
266                 client.Parameters.Clear();
267                 client.Parameters.Add(&quot;format&quot;, &quot;json&quot;);
268                 client.IfModifiedSince = since;
269                 var content = client.DownloadStringWithRetry(container, 3);
270
271                 client.AssertStatusOK(&quot;ListObjects failed&quot;);
272
273                 var infos = JsonConvert.DeserializeObject&lt;IList&lt;ObjectInfo&gt;&gt;(content);
274
275                 Trace.TraceInformation(&quot;[END] ListObjects&quot;);
276                 return infos;
277             }
278         }
279
280
281
282         public IList&lt;ObjectInfo&gt; ListObjects(string container, string folder, DateTime? since = null)
283         {
284             if (String.IsNullOrWhiteSpace(container))
285                 throw new ArgumentNullException(&quot;container&quot;);
286             if (String.IsNullOrWhiteSpace(folder))
287                 throw new ArgumentNullException(&quot;folder&quot;);
288             Contract.EndContractBlock();
289
290             Trace.TraceInformation(&quot;[START] ListObjects&quot;);
291
292             using (var client = new RestClient(_baseClient))
293             {
294                 client.Parameters.Clear();
295                 client.Parameters.Add(&quot;format&quot;, &quot;json&quot;);
296                 client.Parameters.Add(&quot;path&quot;, folder);
297                 client.IfModifiedSince = since;
298                 var content = client.DownloadStringWithRetry(container, 3);
299                 client.AssertStatusOK(&quot;ListObjects failed&quot;);
300
301                 var infos = JsonConvert.DeserializeObject&lt;IList&lt;ObjectInfo&gt;&gt;(content);
302
303                 Trace.TraceInformation(&quot;[END] ListObjects&quot;);
304                 return infos;
305             }
306         }
307
308  
309         public bool ContainerExists(string container)
310         {
311             if (String.IsNullOrWhiteSpace(container))
312                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
313             using (var client = new RestClient(_baseClient))
314             {
315                 client.Parameters.Clear();
316                 client.Head(container, 3);
317
318                 switch (client.StatusCode)
319                 {
320                     case HttpStatusCode.OK:
321                     case HttpStatusCode.NoContent:
322                         return true;
323                     case HttpStatusCode.NotFound:
324                         return false;
325                     default:
326                         throw CreateWebException(&quot;ContainerExists&quot;, client.StatusCode);
327                 }
328             }
329         }
330
331         public bool ObjectExists(string container,string objectName)
332         {
333             if (String.IsNullOrWhiteSpace(container))
334                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
335             if (String.IsNullOrWhiteSpace(objectName))
336                 throw new ArgumentNullException(&quot;objectName&quot;, &quot;The objectName property can&#39;t be empty&quot;);
337             using (var client = new RestClient(_baseClient))
338             {
339                 client.Parameters.Clear();
340                 client.Head(container + &quot;/&quot; + objectName, 3);
341
342                 switch (client.StatusCode)
343                 {
344                     case HttpStatusCode.OK:
345                     case HttpStatusCode.NoContent:
346                         return true;
347                     case HttpStatusCode.NotFound:
348                         return false;
349                     default:
350                         throw CreateWebException(&quot;ObjectExists&quot;, client.StatusCode);
351                 }
352             }
353
354         }
355
356         public ObjectInfo GetObjectInfo(string container, string objectName)
357         {
358             if (String.IsNullOrWhiteSpace(container))
359                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
360             if (String.IsNullOrWhiteSpace(objectName))
361                 throw new ArgumentNullException(&quot;objectName&quot;, &quot;The objectName property can&#39;t be empty&quot;);
362
363             using (var client = new RestClient(_baseClient))
364             {
365                 try
366                 {
367                     client.Parameters.Clear();
368
369                     client.Head(container + &quot;/&quot; + objectName, 3);
370
371                     if (client.TimedOut)
372                         return ObjectInfo.Empty;
373
374                     switch (client.StatusCode)
375                     {
376                         case HttpStatusCode.OK:
377                         case HttpStatusCode.NoContent:
378                             var keys = client.ResponseHeaders.AllKeys.AsQueryable();
379                             var tags = (from key in keys
380                                         where key.StartsWith(&quot;X-Object-Meta-&quot;)
381                                         let name = key.Substring(14)
382                                         select new {Name = name, Value = client.ResponseHeaders[name]})
383                                 .ToDictionary(t =&gt; t.Name, t =&gt; t.Value);
384                             var extensions = (from key in keys
385                                               where key.StartsWith(&quot;X-Object-&quot;) &amp;&amp; !key.StartsWith(&quot;X-Object-Meta-&quot;)
386                                               let name = key.Substring(9)
387                                               select new {Name = name, Value = client.ResponseHeaders[name]})
388                                 .ToDictionary(t =&gt; t.Name, t =&gt; t.Value);
389                             return new ObjectInfo
390                                        {
391                                            Name = objectName,
392                                            /*Bytes =
393                                                long.Parse(client.GetHeaderValue(&quot;Content-Length&quot;)),*/
394                                            Hash = client.GetHeaderValue(&quot;ETag&quot;),
395                                            Content_Type = client.GetHeaderValue(&quot;Content-Type&quot;),
396                                            Tags = tags,
397                                            Extensions = extensions
398                                        };
399                         case HttpStatusCode.NotFound:
400                             return ObjectInfo.Empty;
401                         default:
402                             throw new WebException(
403                                 String.Format(&quot;[FAIL] GetObjectInfo for {0} failed with unexpected status code {1}&quot;,
404                                               objectName, client.StatusCode));
405                     }
406
407                 }
408                 catch(RetryException e)
409                 {
410                     Trace.TraceWarning(&quot;[RETRY FAIL] GetObjectInfo for {0} failed.&quot;);
411                     return ObjectInfo.Empty;
412                 }
413                 catch(WebException e)
414                 {
415                     Trace.TraceError(
416                         String.Format(&quot;[FAIL] GetObjectInfo for {0} failed with unexpected status code {1}&quot;,
417                                       objectName, client.StatusCode), e);
418                     throw;
419                 }
420             }
421
422         }
423
424         public void CreateFolder(string container, string folder)
425         {
426             if (String.IsNullOrWhiteSpace(container))
427                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
428             if (String.IsNullOrWhiteSpace(folder))
429                 throw new ArgumentNullException(&quot;folder&quot;, &quot;The folder property can&#39;t be empty&quot;);
430
431             var folderUrl=String.Format(&quot;{0}/{1}&quot;,container,folder);
432             using (var client = new RestClient(_baseClient))
433             {
434                 client.Parameters.Clear();
435                 client.Headers.Add(&quot;Content-Type&quot;, @&quot;application/directory&quot;);
436                 client.Headers.Add(&quot;Content-Length&quot;, &quot;0&quot;);
437                 client.PutWithRetry(folderUrl, 3);
438
439                 if (client.StatusCode != HttpStatusCode.Created &amp;&amp; client.StatusCode != HttpStatusCode.Accepted)
440                     throw CreateWebException(&quot;CreateFolder&quot;, client.StatusCode);
441             }
442         }
443
444         public ContainerInfo GetContainerInfo(string container)
445         {
446             if (String.IsNullOrWhiteSpace(container))
447                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
448             using (var client = new RestClient(_baseClient))
449             {
450                 client.Head(container);
451                 switch (client.StatusCode)
452                 {
453                     case HttpStatusCode.NoContent:
454                         var containerInfo = new ContainerInfo
455                                                 {
456                                                     Name = container,
457                                                     Count =
458                                                         long.Parse(client.GetHeaderValue(&quot;X-Container-Object-Count&quot;)),
459                                                     Bytes = long.Parse(client.GetHeaderValue(&quot;X-Container-Bytes-Used&quot;))
460                                                 };
461                         return containerInfo;
462                     case HttpStatusCode.NotFound:
463                         return ContainerInfo.Empty;
464                     default:
465                         throw CreateWebException(&quot;GetContainerInfo&quot;, client.StatusCode);
466                 }
467             }
468         }
469
470         public void CreateContainer(string container)
471         {
472             if (String.IsNullOrWhiteSpace(container))
473                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
474             using (var client = new RestClient(_baseClient))
475             {
476                 client.PutWithRetry(container, 3);
477                 var expectedCodes = new[] {HttpStatusCode.Created, HttpStatusCode.Accepted, HttpStatusCode.OK};
478                 if (!expectedCodes.Contains(client.StatusCode))
479                     throw CreateWebException(&quot;CreateContainer&quot;, client.StatusCode);
480             }
481         }
482
483         public void DeleteContainer(string container)
484         {
485             if (String.IsNullOrWhiteSpace(container))
486                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
487             using (var client = new RestClient(_baseClient))
488             {
489                 client.DeleteWithRetry(container, 3);
490                 var expectedCodes = new[] {HttpStatusCode.NotFound, HttpStatusCode.NoContent};
491                 if (!expectedCodes.Contains(client.StatusCode))
492                     throw CreateWebException(&quot;DeleteContainer&quot;, client.StatusCode);
493             }
494
495         }
496
497 </code></pre>
498                                                 </td>
499                                         </tr>
500                                         <tr id="section_10">
501                                                 <td class="docs">
502                                                         <div class="pilwrap">
503                                                                 <a class="pilcrow" href="#section_10">&#182;</a>
504                                                         </div>
505                                                         <p>/ <summary>
506
507 / </summary>
508 / <param name="container"></param>
509 / <param name="objectName"></param>
510 / <param name="fileName"></param>
511 / <returns></returns>
512 / <remarks>This method should have no timeout or a very long one</remarks>
513 Asynchronously download the object specified by <em>objectName</em> in a specific <em>container</em> to 
514 a local file</p>
515
516                                                 </td>
517                                                 <td class="code">
518                                                         <pre><code class='prettyprint'>        public Task GetObject(string container, string objectName, string fileName)
519         {
520             if (String.IsNullOrWhiteSpace(container))
521                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
522             if (String.IsNullOrWhiteSpace(objectName))
523                 throw new ArgumentNullException(&quot;objectName&quot;, &quot;The objectName property can&#39;t be empty&quot;);            
524             Contract.EndContractBlock();
525
526             try
527             {
528 </code></pre>
529                                                 </td>
530                                         </tr>
531                                         <tr id="section_11">
532                                                 <td class="docs">
533                                                         <div class="pilwrap">
534                                                                 <a class="pilcrow" href="#section_11">&#182;</a>
535                                                         </div>
536                                                         <p>The container and objectName are relative names. They are joined with the client's
537 BaseAddress to create the object's absolute address</p>
538
539                                                 </td>
540                                                 <td class="code">
541                                                         <pre><code class='prettyprint'>                var url = String.Join(&quot;/&quot;, _baseClient.BaseAddress, container, objectName);
542                 var uri = new Uri(url);
543 </code></pre>
544                                                 </td>
545                                         </tr>
546                                         <tr id="section_12">
547                                                 <td class="docs">
548                                                         <div class="pilwrap">
549                                                                 <a class="pilcrow" href="#section_12">&#182;</a>
550                                                         </div>
551                                                         <p>WebClient, and by extension RestClient, are not thread-safe. Create a new RestClient
552 object to avoid concurrency errors.</p>
553
554 <p>Download operations take a long time therefore they have no timeout.</p>
555
556                                                 </td>
557                                                 <td class="code">
558                                                         <pre><code class='prettyprint'>                var client = new RestClient(_baseClient) { Timeout = 0 };
559                
560 </code></pre>
561                                                 </td>
562                                         </tr>
563                                         <tr id="section_13">
564                                                 <td class="docs">
565                                                         <div class="pilwrap">
566                                                                 <a class="pilcrow" href="#section_13">&#182;</a>
567                                                         </div>
568                                                         <p>Download progress is reported to the Trace log</p>
569
570                                                 </td>
571                                                 <td class="code">
572                                                         <pre><code class='prettyprint'>                Trace.TraceInformation(&quot;[GET] START {0}&quot;, objectName);
573                 client.DownloadProgressChanged += (sender, args) =&gt; 
574                     Trace.TraceInformation(&quot;[GET PROGRESS] {0} {1}% {2} of {3}&quot;,
575                                     fileName, args.ProgressPercentage,
576                                     args.BytesReceived,
577                                     args.TotalBytesToReceive);                
578
579 </code></pre>
580                                                 </td>
581                                         </tr>
582                                         <tr id="section_14">
583                                                 <td class="docs">
584                                                         <div class="pilwrap">
585                                                                 <a class="pilcrow" href="#section_14">&#182;</a>
586                                                         </div>
587                                                         <p>Start downloading the object asynchronously</p>
588
589                                                 </td>
590                                                 <td class="code">
591                                                         <pre><code class='prettyprint'>                var downloadTask = client.DownloadFileTask(uri, fileName);
592                 
593 </code></pre>
594                                                 </td>
595                                         </tr>
596                                         <tr id="section_15">
597                                                 <td class="docs">
598                                                         <div class="pilwrap">
599                                                                 <a class="pilcrow" href="#section_15">&#182;</a>
600                                                         </div>
601                                                         <p>Once the download completes</p>
602
603                                                 </td>
604                                                 <td class="code">
605                                                         <pre><code class='prettyprint'>                return downloadTask.ContinueWith(download =&gt;
606                                       {
607 </code></pre>
608                                                 </td>
609                                         </tr>
610                                         <tr id="section_16">
611                                                 <td class="docs">
612                                                         <div class="pilwrap">
613                                                                 <a class="pilcrow" href="#section_16">&#182;</a>
614                                                         </div>
615                                                         <p>Delete the local client object</p>
616
617                                                 </td>
618                                                 <td class="code">
619                                                         <pre><code class='prettyprint'>                                          client.Dispose();
620 </code></pre>
621                                                 </td>
622                                         </tr>
623                                         <tr id="section_17">
624                                                 <td class="docs">
625                                                         <div class="pilwrap">
626                                                                 <a class="pilcrow" href="#section_17">&#182;</a>
627                                                         </div>
628                                                         <p>And report failure or completion</p>
629
630                                                 </td>
631                                                 <td class="code">
632                                                         <pre><code class='prettyprint'>                                          if (download.IsFaulted)
633                                           {
634                                               Trace.TraceError(&quot;[GET] FAIL for {0} with \r{1}&quot;, objectName,
635                                                                download.Exception);
636                                           }
637                                           else
638                                           {
639                                               Trace.TraceInformation(&quot;[GET] END {0}&quot;, objectName);                                             
640                                           }
641                                       });
642             }
643             catch (Exception exc)
644             {
645                 Trace.TraceError(&quot;[GET] END {0} with {1}&quot;, objectName, exc);
646                 throw;
647             }
648
649
650
651         }
652
653 </code></pre>
654                                                 </td>
655                                         </tr>
656                                         <tr id="section_18">
657                                                 <td class="docs">
658                                                         <div class="pilwrap">
659                                                                 <a class="pilcrow" href="#section_18">&#182;</a>
660                                                         </div>
661                                                         <p>/ <summary>
662
663 / </summary>
664 / <param name="container"></param>
665 / <param name="objectName"></param>
666 / <param name="fileName"></param>
667 / <param name="hash">Optional hash value for the file. If no hash is provided, the method calculates a new hash</param>
668 / <remarks>>This method should have no timeout or a very long one</remarks></p>
669
670                                                 </td>
671                                                 <td class="code">
672                                                         <pre><code class='prettyprint'>        public Task PutObject(string container, string objectName, string fileName, string hash = null)
673         {
674             if (String.IsNullOrWhiteSpace(container))
675                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
676             if (String.IsNullOrWhiteSpace(objectName))
677                 throw new ArgumentNullException(&quot;objectName&quot;, &quot;The objectName property can&#39;t be empty&quot;);
678             if (String.IsNullOrWhiteSpace(fileName))
679                 throw new ArgumentNullException(&quot;fileName&quot;, &quot;The fileName property can&#39;t be empty&quot;);
680             if (!File.Exists(fileName))
681                 throw new FileNotFoundException(&quot;The file does not exist&quot;,fileName);
682
683             
684             try
685             {
686                 var url = String.Join(&quot;/&quot;,_baseClient.BaseAddress,container,objectName);
687                 var uri = new Uri(url);
688
689                 var client = new RestClient(_baseClient){Timeout=0};           
690                 string etag = hash ?? CalculateHash(fileName);
691
692                 client.Headers.Add(&quot;Content-Type&quot;, &quot;application/octet-stream&quot;);
693                 client.Headers.Add(&quot;ETag&quot;, etag);
694
695
696                 Trace.TraceInformation(&quot;[PUT] START {0}&quot;, objectName);
697                 client.UploadProgressChanged += (sender, args) =&gt;
698                 {
699                     Trace.TraceInformation(&quot;[PUT PROGRESS] {0} {1}% {2} of {3}&quot;, fileName, args.ProgressPercentage, args.BytesSent, args.TotalBytesToSend);
700                 };
701                
702                 return client.UploadFileTask(uri, &quot;PUT&quot;, fileName)
703                     .ContinueWith(upload=&gt;
704                                       {
705                                           client.Dispose();
706
707                                           if (upload.IsFaulted)
708                                           {                                              
709                                               Trace.TraceError(&quot;[PUT] FAIL for {0} with \r{1}&quot;,objectName,upload.Exception);
710                                           }
711                                           else
712                                             Trace.TraceInformation(&quot;[PUT] END {0}&quot;, objectName);
713                                       });
714             }
715             catch (Exception exc)
716             {
717                 Trace.TraceError(&quot;[PUT] END {0} with {1}&quot;, objectName, exc);
718                 throw;
719             }                
720
721         }
722        
723         
724         private static string CalculateHash(string fileName)
725         {
726             string hash;
727             using (var hasher = MD5.Create())
728             using(var stream=File.OpenRead(fileName))
729             {
730                 var hashBuilder=new StringBuilder();
731                 foreach (byte b in hasher.ComputeHash(stream))
732                     hashBuilder.Append(b.ToString(&quot;x2&quot;).ToLower());
733                 hash = hashBuilder.ToString();                
734             }
735             return hash;
736         }
737
738         public void DeleteObject(string container, string objectName)
739         {
740             if (String.IsNullOrWhiteSpace(container))
741                 throw new ArgumentNullException(&quot;container&quot;, &quot;The container property can&#39;t be empty&quot;);
742             if (String.IsNullOrWhiteSpace(objectName))
743                 throw new ArgumentNullException(&quot;objectName&quot;, &quot;The objectName property can&#39;t be empty&quot;);
744             using (var client = new RestClient(_baseClient))
745             {
746
747                 client.DeleteWithRetry(container + &quot;/&quot; + objectName, 3);
748
749                 var expectedCodes = new[] {HttpStatusCode.NotFound, HttpStatusCode.NoContent};
750                 if (!expectedCodes.Contains(client.StatusCode))
751                     throw CreateWebException(&quot;DeleteObject&quot;, client.StatusCode);
752             }
753
754         }
755
756         public void MoveObject(string sourceContainer, string oldObjectName, string targetContainer,string newObjectName)
757         {
758             if (String.IsNullOrWhiteSpace(sourceContainer))
759                 throw new ArgumentNullException(&quot;sourceContainer&quot;, &quot;The container property can&#39;t be empty&quot;);
760             if (String.IsNullOrWhiteSpace(oldObjectName))
761                 throw new ArgumentNullException(&quot;oldObjectName&quot;, &quot;The oldObjectName property can&#39;t be empty&quot;);
762             if (String.IsNullOrWhiteSpace(targetContainer))
763                 throw new ArgumentNullException(&quot;targetContainer&quot;, &quot;The container property can&#39;t be empty&quot;);
764             if (String.IsNullOrWhiteSpace(newObjectName))
765                 throw new ArgumentNullException(&quot;newObjectName&quot;, &quot;The newObjectName property can&#39;t be empty&quot;);
766
767             var targetUrl = targetContainer + &quot;/&quot; + newObjectName;
768             var sourceUrl = String.Format(&quot;/{0}/{1}&quot;, sourceContainer, oldObjectName);
769
770             using (var client = new RestClient(_baseClient))
771             {
772                 client.Headers.Add(&quot;X-Copy-From&quot;, sourceUrl);
773                 client.PutWithRetry(targetUrl, 3);
774
775                 var expectedCodes = new[] {HttpStatusCode.OK, HttpStatusCode.NoContent, HttpStatusCode.Created};
776                 if (expectedCodes.Contains(client.StatusCode))
777                 {
778                     this.DeleteObject(sourceContainer, oldObjectName);
779                 }
780                 else
781                     throw CreateWebException(&quot;MoveObject&quot;, client.StatusCode);
782             }
783         }
784
785       
786         private static WebException CreateWebException(string operation, HttpStatusCode statusCode)
787         {
788             return new WebException(String.Format(&quot;{0} failed with unexpected status code {1}&quot;, operation, statusCode));
789         }
790
791         
792     }
793 }
794 </code></pre>
795                                                 </td>
796                                         </tr>
797                         </tbody>
798                 </table>
799         </div>
800 </body>
801 </html>