Revision cfed7823 trunk/Pithos.Network/CloudFilesClient.cs
b/trunk/Pithos.Network/CloudFilesClient.cs | ||
---|---|---|
29 | 29 |
//RestClient provides a REST-friendly interface over the standard WebClient. |
30 | 30 |
private RestClient _baseClient; |
31 | 31 |
|
32 |
//Some operations can specify a Timeout. The default value of all timeouts is 10 seconds |
|
33 |
private readonly TimeSpan _shortTimeout = TimeSpan.FromSeconds(10); |
|
34 |
|
|
35 |
//Some operations can be retried before failing. The default number of retries is 5 |
|
36 |
private readonly int _retries = 5; |
|
37 |
|
|
38 | 32 |
//During authentication the client provides a UserName |
39 | 33 |
public string UserName { get; set; } |
40 | 34 |
|
... | ... | |
48 | 42 |
//The client also receives a StorageUrl after authentication. All subsequent operations must |
49 | 43 |
//use this url |
50 | 44 |
public Uri StorageUrl { get; set; } |
51 |
|
|
45 |
|
|
46 |
protected Uri RootAddressUri { get; set; } |
|
47 |
|
|
52 | 48 |
public Uri Proxy { get; set; } |
53 | 49 |
|
54 | 50 |
public double DownloadPercentLimit { get; set; } |
... | ... | |
69 | 65 |
// |
70 | 66 |
public void Authenticate(string userName,string apiKey) |
71 | 67 |
{ |
72 |
Trace.TraceInformation("[AUTHENTICATE] Start for {0}", userName); |
|
73 | 68 |
if (String.IsNullOrWhiteSpace(userName)) |
74 | 69 |
throw new ArgumentNullException("userName", "The userName property can't be empty"); |
75 | 70 |
if (String.IsNullOrWhiteSpace(apiKey)) |
76 | 71 |
throw new ArgumentNullException("apiKey", "The apiKey property can't be empty"); |
72 |
Contract.Ensures(_baseClient != null); |
|
73 |
Contract.EndContractBlock(); |
|
74 |
|
|
75 |
Trace.TraceInformation("[AUTHENTICATE] Start for {0}", userName); |
|
77 | 76 |
|
78 | 77 |
if (_authenticated) |
79 | 78 |
return; |
... | ... | |
87 | 86 |
if (Proxy != null) |
88 | 87 |
authClient.Proxy = new WebProxy(Proxy); |
89 | 88 |
|
89 |
Contract.Assume(authClient.Headers!=null); |
|
90 |
|
|
90 | 91 |
authClient.Headers.Add("X-Auth-User", UserName); |
91 | 92 |
authClient.Headers.Add("X-Auth-Key", ApiKey); |
92 | 93 |
|
... | ... | |
99 | 100 |
throw new InvalidOperationException("Failed to obtain storage url"); |
100 | 101 |
StorageUrl = new Uri(storageUrl); |
101 | 102 |
|
103 |
//Get the root address (StorageUrl without the account) |
|
104 |
var usernameIndex=storageUrl.LastIndexOf(UserName); |
|
105 |
var rootUrl = storageUrl.Substring(0, usernameIndex); |
|
106 |
RootAddressUri = new Uri(rootUrl); |
|
107 |
|
|
102 | 108 |
var token = authClient.GetHeaderValue("X-Auth-Token"); |
103 | 109 |
if (String.IsNullOrWhiteSpace(token)) |
104 | 110 |
throw new InvalidOperationException("Failed to obtain token url"); |
... | ... | |
112 | 118 |
if (Proxy!=null) |
113 | 119 |
_baseClient.Proxy = new WebProxy(Proxy); |
114 | 120 |
|
121 |
|
|
122 |
|
|
123 |
Contract.Assume(_baseClient.Headers!=null); |
|
115 | 124 |
_baseClient.Headers.Add("X-Auth-Token", Token); |
116 | 125 |
|
117 | 126 |
Trace.TraceInformation("[AUTHENTICATE] End for {0}", userName); |
118 | 127 |
} |
119 | 128 |
|
120 | 129 |
|
121 |
public IList<ContainerInfo> ListContainers() |
|
130 |
|
|
131 |
public IList<ContainerInfo> ListContainers(string account) |
|
122 | 132 |
{ |
133 |
|
|
123 | 134 |
using (var client = new RestClient(_baseClient)) |
124 | 135 |
{ |
136 |
if (!String.IsNullOrWhiteSpace(account)) |
|
137 |
client.BaseAddress = GetAccountUrl(account); |
|
138 |
|
|
125 | 139 |
client.Parameters.Clear(); |
126 | 140 |
client.Parameters.Add("format", "json"); |
127 | 141 |
var content = client.DownloadStringWithRetry("", 3); |
... | ... | |
135 | 149 |
|
136 | 150 |
} |
137 | 151 |
|
152 |
private string GetAccountUrl(string account) |
|
153 |
{ |
|
154 |
return new Uri(this.RootAddressUri, new Uri(account,UriKind.Relative)).AbsoluteUri; |
|
155 |
} |
|
156 |
|
|
157 |
public IList<ShareAccountInfo> ListSharingAccounts(DateTime? since=null) |
|
158 |
{ |
|
159 |
Trace.TraceInformation("[START] ListSharingAccounts"); |
|
160 |
|
|
161 |
using (var client = new RestClient(_baseClient)) |
|
162 |
{ |
|
163 |
client.Parameters.Clear(); |
|
164 |
client.Parameters.Add("format", "json"); |
|
165 |
client.IfModifiedSince = since; |
|
166 |
|
|
167 |
//Extract the username from the base address |
|
168 |
client.BaseAddress = RootAddressUri.AbsoluteUri; |
|
169 |
|
|
170 |
var content = client.DownloadStringWithRetry(@"", 3); |
|
171 |
|
|
172 |
client.AssertStatusOK("ListSharingAccounts failed"); |
|
173 |
|
|
174 |
//If the result is empty, return an empty list, |
|
175 |
var infos = String.IsNullOrWhiteSpace(content) |
|
176 |
? new List<ShareAccountInfo>() |
|
177 |
//Otherwise deserialize the account list into a list of ShareAccountInfos |
|
178 |
: JsonConvert.DeserializeObject<IList<ShareAccountInfo>>(content); |
|
179 |
|
|
180 |
Trace.TraceInformation("[END] ListSharingAccounts"); |
|
181 |
return infos; |
|
182 |
} |
|
183 |
} |
|
184 |
|
|
138 | 185 |
//Request listing of all objects in a container modified since a specific time. |
139 | 186 |
//If the *since* value is missing, return all objects |
140 |
public IList<ObjectInfo> ListObjects(string container, DateTime? since = null) |
|
187 |
public IList<ObjectInfo> ListSharedObjects(DateTime? since = null) |
|
188 |
{ |
|
189 |
|
|
190 |
Trace.TraceInformation("[START] ListSharedObjects"); |
|
191 |
|
|
192 |
var objects=new List<ObjectInfo>(); |
|
193 |
var accounts=ListSharingAccounts(since); |
|
194 |
foreach (var account in accounts) |
|
195 |
{ |
|
196 |
var containers=ListContainers(account.name); |
|
197 |
foreach (var container in containers) |
|
198 |
{ |
|
199 |
var containerObjects=ListObjects(account.name, container.Name, account.last_modified); |
|
200 |
objects.AddRange(containerObjects); |
|
201 |
} |
|
202 |
} |
|
203 |
return objects; |
|
204 |
} |
|
205 |
|
|
206 |
public void ShareObject(string account, string container, string objectName, string shareTo, bool read, bool write) |
|
207 |
{ |
|
208 |
if (String.IsNullOrWhiteSpace(Token)) |
|
209 |
throw new InvalidOperationException("The Token is not set"); |
|
210 |
if (StorageUrl==null) |
|
211 |
throw new InvalidOperationException("The StorageUrl is not set"); |
|
212 |
if (String.IsNullOrWhiteSpace(container)) |
|
213 |
throw new ArgumentNullException("container"); |
|
214 |
if (String.IsNullOrWhiteSpace(objectName)) |
|
215 |
throw new ArgumentNullException("objectName"); |
|
216 |
if (String.IsNullOrWhiteSpace(account)) |
|
217 |
throw new ArgumentNullException("account"); |
|
218 |
if (String.IsNullOrWhiteSpace(shareTo)) |
|
219 |
throw new ArgumentNullException("shareTo"); |
|
220 |
Contract.EndContractBlock(); |
|
221 |
|
|
222 |
using (var client = new RestClient(_baseClient)) |
|
223 |
{ |
|
224 |
|
|
225 |
client.BaseAddress = GetAccountUrl(account); |
|
226 |
|
|
227 |
client.Parameters.Clear(); |
|
228 |
client.Parameters.Add("format", "json"); |
|
229 |
|
|
230 |
string permission = ""; |
|
231 |
if (write) |
|
232 |
permission = String.Format("write={0}", shareTo); |
|
233 |
else if (read) |
|
234 |
permission=String.Format("read={0}", shareTo); |
|
235 |
client.Headers.Add("X-Object-Sharing",permission); |
|
236 |
|
|
237 |
var content = client.DownloadStringWithRetry(container, 3); |
|
238 |
|
|
239 |
client.AssertStatusOK("ShareObject failed"); |
|
240 |
|
|
241 |
//If the result is empty, return an empty list, |
|
242 |
var infos = String.IsNullOrWhiteSpace(content) |
|
243 |
? new List<ObjectInfo>() |
|
244 |
//Otherwise deserialize the object list into a list of ObjectInfos |
|
245 |
: JsonConvert.DeserializeObject<IList<ObjectInfo>>(content); |
|
246 |
|
|
247 |
Trace.TraceInformation("[END] ListObjects"); |
|
248 |
} |
|
249 |
|
|
250 |
|
|
251 |
} |
|
252 |
|
|
253 |
|
|
254 |
public IList<ObjectInfo> ListObjects(string account, string container, DateTime? since = null) |
|
141 | 255 |
{ |
142 | 256 |
if (String.IsNullOrWhiteSpace(container)) |
143 | 257 |
throw new ArgumentNullException("container"); |
... | ... | |
147 | 261 |
|
148 | 262 |
using (var client = new RestClient(_baseClient)) |
149 | 263 |
{ |
264 |
if (!String.IsNullOrWhiteSpace(account)) |
|
265 |
client.BaseAddress = GetAccountUrl(account); |
|
266 |
|
|
150 | 267 |
client.Parameters.Clear(); |
151 | 268 |
client.Parameters.Add("format", "json"); |
152 | 269 |
client.IfModifiedSince = since; |
... | ... | |
160 | 277 |
//Otherwise deserialize the object list into a list of ObjectInfos |
161 | 278 |
: JsonConvert.DeserializeObject<IList<ObjectInfo>>(content); |
162 | 279 |
|
280 |
foreach (var info in infos) |
|
281 |
{ |
|
282 |
info.Container = container; |
|
283 |
info.Account = account; |
|
284 |
} |
|
163 | 285 |
Trace.TraceInformation("[END] ListObjects"); |
164 | 286 |
return infos; |
165 | 287 |
} |
... | ... | |
167 | 289 |
|
168 | 290 |
|
169 | 291 |
|
170 |
public IList<ObjectInfo> ListObjects(string container, string folder, DateTime? since = null) |
|
292 |
public IList<ObjectInfo> ListObjects(string account, string container, string folder, DateTime? since = null)
|
|
171 | 293 |
{ |
172 | 294 |
if (String.IsNullOrWhiteSpace(container)) |
173 | 295 |
throw new ArgumentNullException("container"); |
... | ... | |
179 | 301 |
|
180 | 302 |
using (var client = new RestClient(_baseClient)) |
181 | 303 |
{ |
304 |
if (!String.IsNullOrWhiteSpace(account)) |
|
305 |
client.BaseAddress = GetAccountUrl(account); |
|
306 |
|
|
182 | 307 |
client.Parameters.Clear(); |
183 | 308 |
client.Parameters.Add("format", "json"); |
184 | 309 |
client.Parameters.Add("path", folder); |
... | ... | |
194 | 319 |
} |
195 | 320 |
|
196 | 321 |
|
197 |
public bool ContainerExists(string container) |
|
322 |
public bool ContainerExists(string account, string container)
|
|
198 | 323 |
{ |
199 | 324 |
if (String.IsNullOrWhiteSpace(container)) |
200 | 325 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
326 |
Contract.EndContractBlock(); |
|
327 |
|
|
201 | 328 |
using (var client = new RestClient(_baseClient)) |
202 | 329 |
{ |
330 |
if (!String.IsNullOrWhiteSpace(account)) |
|
331 |
client.BaseAddress = GetAccountUrl(account); |
|
332 |
|
|
203 | 333 |
client.Parameters.Clear(); |
204 | 334 |
client.Head(container, 3); |
205 | 335 |
|
... | ... | |
216 | 346 |
} |
217 | 347 |
} |
218 | 348 |
|
219 |
public bool ObjectExists(string container,string objectName)
|
|
349 |
public bool ObjectExists(string account, string container, string objectName)
|
|
220 | 350 |
{ |
221 | 351 |
if (String.IsNullOrWhiteSpace(container)) |
222 | 352 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
223 | 353 |
if (String.IsNullOrWhiteSpace(objectName)) |
224 | 354 |
throw new ArgumentNullException("objectName", "The objectName property can't be empty"); |
355 |
Contract.EndContractBlock(); |
|
356 |
|
|
225 | 357 |
using (var client = new RestClient(_baseClient)) |
226 | 358 |
{ |
359 |
if (!String.IsNullOrWhiteSpace(account)) |
|
360 |
client.BaseAddress = GetAccountUrl(account); |
|
361 |
|
|
227 | 362 |
client.Parameters.Clear(); |
228 | 363 |
client.Head(container + "/" + objectName, 3); |
229 | 364 |
|
... | ... | |
241 | 376 |
|
242 | 377 |
} |
243 | 378 |
|
244 |
public ObjectInfo GetObjectInfo(string container, string objectName) |
|
379 |
public ObjectInfo GetObjectInfo(string account, string container, string objectName)
|
|
245 | 380 |
{ |
246 | 381 |
if (String.IsNullOrWhiteSpace(container)) |
247 | 382 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
248 | 383 |
if (String.IsNullOrWhiteSpace(objectName)) |
249 | 384 |
throw new ArgumentNullException("objectName", "The objectName property can't be empty"); |
385 |
Contract.EndContractBlock(); |
|
250 | 386 |
|
251 | 387 |
using (var client = new RestClient(_baseClient)) |
252 | 388 |
{ |
389 |
if (!String.IsNullOrWhiteSpace(account)) |
|
390 |
client.BaseAddress = GetAccountUrl(account); |
|
253 | 391 |
try |
254 | 392 |
{ |
255 | 393 |
client.Parameters.Clear(); |
... | ... | |
308 | 446 |
|
309 | 447 |
} |
310 | 448 |
|
311 |
public void CreateFolder(string container, string folder) |
|
449 |
public void CreateFolder(string account, string container, string folder)
|
|
312 | 450 |
{ |
313 | 451 |
if (String.IsNullOrWhiteSpace(container)) |
314 | 452 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
315 | 453 |
if (String.IsNullOrWhiteSpace(folder)) |
316 | 454 |
throw new ArgumentNullException("folder", "The folder property can't be empty"); |
455 |
Contract.EndContractBlock(); |
|
317 | 456 |
|
318 | 457 |
var folderUrl=String.Format("{0}/{1}",container,folder); |
319 | 458 |
using (var client = new RestClient(_baseClient)) |
320 | 459 |
{ |
460 |
if (!String.IsNullOrWhiteSpace(account)) |
|
461 |
client.BaseAddress = GetAccountUrl(account); |
|
462 |
|
|
321 | 463 |
client.Parameters.Clear(); |
322 | 464 |
client.Headers.Add("Content-Type", @"application/directory"); |
323 | 465 |
client.Headers.Add("Content-Length", "0"); |
... | ... | |
328 | 470 |
} |
329 | 471 |
} |
330 | 472 |
|
331 |
public ContainerInfo GetContainerInfo(string container) |
|
473 |
public ContainerInfo GetContainerInfo(string account, string container)
|
|
332 | 474 |
{ |
333 | 475 |
if (String.IsNullOrWhiteSpace(container)) |
334 | 476 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
477 |
Contract.EndContractBlock(); |
|
478 |
|
|
335 | 479 |
using (var client = new RestClient(_baseClient)) |
336 | 480 |
{ |
481 |
if (!String.IsNullOrWhiteSpace(account)) |
|
482 |
client.BaseAddress = GetAccountUrl(account); |
|
483 |
|
|
337 | 484 |
client.Head(container); |
338 | 485 |
switch (client.StatusCode) |
339 | 486 |
{ |
... | ... | |
357 | 504 |
} |
358 | 505 |
} |
359 | 506 |
|
360 |
public void CreateContainer(string container) |
|
361 |
{ |
|
507 |
public void CreateContainer(string account, string container)
|
|
508 |
{
|
|
362 | 509 |
if (String.IsNullOrWhiteSpace(container)) |
363 | 510 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
511 |
Contract.EndContractBlock(); |
|
512 |
|
|
364 | 513 |
using (var client = new RestClient(_baseClient)) |
365 | 514 |
{ |
515 |
if (!String.IsNullOrWhiteSpace(account)) |
|
516 |
client.BaseAddress = GetAccountUrl(account); |
|
517 |
|
|
366 | 518 |
client.PutWithRetry(container, 3); |
367 | 519 |
var expectedCodes = new[] {HttpStatusCode.Created, HttpStatusCode.Accepted, HttpStatusCode.OK}; |
368 | 520 |
if (!expectedCodes.Contains(client.StatusCode)) |
... | ... | |
370 | 522 |
} |
371 | 523 |
} |
372 | 524 |
|
373 |
public void DeleteContainer(string container) |
|
525 |
public void DeleteContainer(string account, string container)
|
|
374 | 526 |
{ |
375 | 527 |
if (String.IsNullOrWhiteSpace(container)) |
376 | 528 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
529 |
Contract.EndContractBlock(); |
|
530 |
|
|
377 | 531 |
using (var client = new RestClient(_baseClient)) |
378 | 532 |
{ |
533 |
if (!String.IsNullOrWhiteSpace(account)) |
|
534 |
client.BaseAddress = GetAccountUrl(account); |
|
535 |
|
|
379 | 536 |
client.DeleteWithRetry(container, 3); |
380 | 537 |
var expectedCodes = new[] {HttpStatusCode.NotFound, HttpStatusCode.NoContent}; |
381 | 538 |
if (!expectedCodes.Contains(client.StatusCode)) |
... | ... | |
387 | 544 |
/// <summary> |
388 | 545 |
/// |
389 | 546 |
/// </summary> |
547 |
/// <param name="account"></param> |
|
390 | 548 |
/// <param name="container"></param> |
391 | 549 |
/// <param name="objectName"></param> |
392 | 550 |
/// <param name="fileName"></param> |
... | ... | |
394 | 552 |
/// <remarks>This method should have no timeout or a very long one</remarks> |
395 | 553 |
//Asynchronously download the object specified by *objectName* in a specific *container* to |
396 | 554 |
// a local file |
397 |
public Task GetObject(string container, string objectName, string fileName) |
|
555 |
public Task GetObject(string account, string container, string objectName, string fileName)
|
|
398 | 556 |
{ |
399 | 557 |
if (String.IsNullOrWhiteSpace(container)) |
400 | 558 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
... | ... | |
404 | 562 |
|
405 | 563 |
try |
406 | 564 |
{ |
407 |
//The container and objectName are relative names. They are joined with the client's |
|
408 |
//BaseAddress to create the object's absolute address |
|
409 |
var builder = GetAddressBuilder(container, objectName); |
|
410 |
var uri = builder.Uri; |
|
411 | 565 |
//WebClient, and by extension RestClient, are not thread-safe. Create a new RestClient |
412 | 566 |
//object to avoid concurrency errors. |
413 | 567 |
// |
414 | 568 |
//Download operations take a long time therefore they have no timeout. |
415 | 569 |
var client = new RestClient(_baseClient) { Timeout = 0 }; |
416 |
|
|
570 |
if (!String.IsNullOrWhiteSpace(account)) |
|
571 |
client.BaseAddress = GetAccountUrl(account); |
|
572 |
|
|
573 |
//The container and objectName are relative names. They are joined with the client's |
|
574 |
//BaseAddress to create the object's absolute address |
|
575 |
var builder = client.GetAddressBuilder(container, objectName); |
|
576 |
var uri = builder.Uri; |
|
577 |
|
|
417 | 578 |
//Download progress is reported to the Trace log |
418 | 579 |
Trace.TraceInformation("[GET] START {0}", objectName); |
419 | 580 |
client.DownloadProgressChanged += (sender, args) => |
... | ... | |
453 | 614 |
|
454 | 615 |
} |
455 | 616 |
|
456 |
public Task<IList<string>> PutHashMap(string container, string objectName, TreeHash hash) |
|
617 |
public Task<IList<string>> PutHashMap(string account, string container, string objectName, TreeHash hash)
|
|
457 | 618 |
{ |
458 | 619 |
if (String.IsNullOrWhiteSpace(container)) |
459 | 620 |
throw new ArgumentNullException("container"); |
... | ... | |
466 | 627 |
if (StorageUrl == null) |
467 | 628 |
throw new InvalidOperationException("Invalid Storage Url"); |
468 | 629 |
Contract.EndContractBlock(); |
630 |
|
|
631 |
|
|
632 |
//Don't use a timeout because putting the hashmap may be a long process |
|
633 |
var client = new RestClient(_baseClient) { Timeout = 0 }; |
|
634 |
if (!String.IsNullOrWhiteSpace(account)) |
|
635 |
client.BaseAddress = GetAccountUrl(account); |
|
636 |
|
|
469 | 637 |
//The container and objectName are relative names. They are joined with the client's |
470 | 638 |
//BaseAddress to create the object's absolute address |
471 |
var builder = GetAddressBuilder(container, objectName); |
|
639 |
var builder = client.GetAddressBuilder(container, objectName);
|
|
472 | 640 |
builder.Query = "format=json&hashmap"; |
473 | 641 |
var uri = builder.Uri; |
474 | 642 |
|
475 |
//Don't use a timeout because putting the hashmap may be a long process |
|
476 |
var client = new RestClient(_baseClient) { Timeout = 0 }; |
|
477 | 643 |
|
478 | 644 |
//Send the tree hash as Json to the server |
479 | 645 |
client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream"; |
... | ... | |
533 | 699 |
|
534 | 700 |
} |
535 | 701 |
|
536 |
public Task<byte[]> GetBlock(string container, Uri relativeUrl, long start, long? end=null)
|
|
702 |
public Task<byte[]> GetBlock(string account, string container, Uri relativeUrl, long start, long? end)
|
|
537 | 703 |
{ |
538 | 704 |
if (String.IsNullOrWhiteSpace(Token)) |
539 | 705 |
throw new InvalidOperationException("Invalid Token"); |
... | ... | |
549 | 715 |
throw new ArgumentOutOfRangeException("start"); |
550 | 716 |
Contract.EndContractBlock(); |
551 | 717 |
|
552 |
var builder = GetAddressBuilder(container, relativeUrl.ToString()); |
|
553 |
|
|
554 |
var uri = builder.Uri; |
|
555 | 718 |
|
556 | 719 |
//Don't use a timeout because putting the hashmap may be a long process |
557 | 720 |
var client = new RestClient(_baseClient) {Timeout = 0, RangeFrom = start, RangeTo = end}; |
721 |
if (!String.IsNullOrWhiteSpace(account)) |
|
722 |
client.BaseAddress = GetAccountUrl(account); |
|
723 |
|
|
724 |
var builder = client.GetAddressBuilder(container, relativeUrl.ToString()); |
|
725 |
var uri = builder.Uri; |
|
726 |
|
|
558 | 727 |
return client.DownloadDataTask(uri) |
559 | 728 |
.ContinueWith(t=> |
560 | 729 |
{ |
... | ... | |
564 | 733 |
} |
565 | 734 |
|
566 | 735 |
|
567 |
public Task PostBlock(string container,byte[] block,int offset,int count)
|
|
736 |
public Task PostBlock(string account, string container, byte[] block, int offset, int count)
|
|
568 | 737 |
{ |
569 | 738 |
if (String.IsNullOrWhiteSpace(container)) |
570 | 739 |
throw new ArgumentNullException("container"); |
... | ... | |
580 | 749 |
throw new InvalidOperationException("Invalid Storage Url"); |
581 | 750 |
Contract.EndContractBlock(); |
582 | 751 |
|
583 |
var builder = GetAddressBuilder(container, ""); |
|
752 |
|
|
753 |
//Don't use a timeout because putting the hashmap may be a long process |
|
754 |
var client = new RestClient(_baseClient) { Timeout = 0 }; |
|
755 |
if (!String.IsNullOrWhiteSpace(account)) |
|
756 |
client.BaseAddress = GetAccountUrl(account); |
|
757 |
|
|
758 |
var builder = client.GetAddressBuilder(container, ""); |
|
584 | 759 |
//We are doing an update |
585 | 760 |
builder.Query = "update"; |
586 | 761 |
var uri = builder.Uri; |
587 |
|
|
588 |
//Don't use a timeout because putting the hashmap may be a long process |
|
589 |
var client = new RestClient(_baseClient) { Timeout = 0 }; |
|
762 |
|
|
590 | 763 |
client.Headers[HttpRequestHeader.ContentType] = "application/octet-stream"; |
591 | 764 |
|
592 | 765 |
Trace.TraceInformation("[BLOCK POST] START"); |
... | ... | |
618 | 791 |
} |
619 | 792 |
|
620 | 793 |
|
621 |
public Task<TreeHash> GetHashMap(string container, string objectName) |
|
794 |
public Task<TreeHash> GetHashMap(string account, string container, string objectName)
|
|
622 | 795 |
{ |
623 | 796 |
if (String.IsNullOrWhiteSpace(container)) |
624 | 797 |
throw new ArgumentNullException("container"); |
... | ... | |
632 | 805 |
|
633 | 806 |
try |
634 | 807 |
{ |
635 |
//The container and objectName are relative names. They are joined with the client's |
|
636 |
//BaseAddress to create the object's absolute address |
|
637 |
var builder = GetAddressBuilder(container, objectName); |
|
638 |
builder.Query="format=json&hashmap"; |
|
639 |
var uri = builder.Uri; |
|
640 | 808 |
//WebClient, and by extension RestClient, are not thread-safe. Create a new RestClient |
641 | 809 |
//object to avoid concurrency errors. |
642 | 810 |
// |
643 | 811 |
//Download operations take a long time therefore they have no timeout. |
644 | 812 |
//TODO: Do we really? this is a hashmap operation, not a download |
645 | 813 |
var client = new RestClient(_baseClient) { Timeout = 0 }; |
646 |
|
|
814 |
if (!String.IsNullOrWhiteSpace(account)) |
|
815 |
client.BaseAddress = GetAccountUrl(account); |
|
816 |
|
|
647 | 817 |
|
818 |
//The container and objectName are relative names. They are joined with the client's |
|
819 |
//BaseAddress to create the object's absolute address |
|
820 |
var builder = client.GetAddressBuilder(container, objectName); |
|
821 |
builder.Query = "format=json&hashmap"; |
|
822 |
var uri = builder.Uri; |
|
823 |
|
|
648 | 824 |
//Start downloading the object asynchronously |
649 | 825 |
var downloadTask = client.DownloadStringTask(uri); |
650 | 826 |
|
... | ... | |
678 | 854 |
|
679 | 855 |
} |
680 | 856 |
|
681 |
private UriBuilder GetAddressBuilder(string container, string objectName) |
|
682 |
{ |
|
683 |
var builder = new UriBuilder(String.Join("/", _baseClient.BaseAddress, container, objectName)); |
|
684 |
return builder; |
|
685 |
} |
|
686 |
|
|
687 | 857 |
|
688 | 858 |
/// <summary> |
689 | 859 |
/// |
690 | 860 |
/// </summary> |
861 |
/// <param name="account"></param> |
|
691 | 862 |
/// <param name="container"></param> |
692 | 863 |
/// <param name="objectName"></param> |
693 | 864 |
/// <param name="fileName"></param> |
694 | 865 |
/// <param name="hash">Optional hash value for the file. If no hash is provided, the method calculates a new hash</param> |
695 | 866 |
/// <remarks>>This method should have no timeout or a very long one</remarks> |
696 |
public Task PutObject(string container, string objectName, string fileName, string hash = null) |
|
867 |
public Task PutObject(string account, string container, string objectName, string fileName, string hash = null)
|
|
697 | 868 |
{ |
698 | 869 |
if (String.IsNullOrWhiteSpace(container)) |
699 | 870 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
... | ... | |
703 | 874 |
throw new ArgumentNullException("fileName", "The fileName property can't be empty"); |
704 | 875 |
if (!File.Exists(fileName)) |
705 | 876 |
throw new FileNotFoundException("The file does not exist",fileName); |
706 |
|
|
877 |
Contract.EndContractBlock(); |
|
707 | 878 |
|
708 | 879 |
try |
709 | 880 |
{ |
710 |
var builder= GetAddressBuilder(container,objectName); |
|
881 |
|
|
882 |
var client = new RestClient(_baseClient){Timeout=0}; |
|
883 |
if (!String.IsNullOrWhiteSpace(account)) |
|
884 |
client.BaseAddress = GetAccountUrl(account); |
|
885 |
|
|
886 |
var builder = client.GetAddressBuilder(container, objectName); |
|
711 | 887 |
var uri = builder.Uri; |
712 | 888 |
|
713 |
var client = new RestClient(_baseClient){Timeout=0}; |
|
714 | 889 |
string etag = hash ?? CalculateHash(fileName); |
715 | 890 |
|
716 | 891 |
client.Headers.Add("Content-Type", "application/octet-stream"); |
... | ... | |
765 | 940 |
return hash; |
766 | 941 |
} |
767 | 942 |
|
768 |
public void DeleteObject(string container, string objectName)
|
|
943 |
/* public void DeleteObject(string container, string objectName,string account)
|
|
769 | 944 |
{ |
770 | 945 |
if (String.IsNullOrWhiteSpace(container)) |
771 | 946 |
throw new ArgumentNullException("container", "The container property can't be empty"); |
772 | 947 |
if (String.IsNullOrWhiteSpace(objectName)) |
773 | 948 |
throw new ArgumentNullException("objectName", "The objectName property can't be empty"); |
949 |
Contract.EndContractBlock(); |
|
950 |
|
|
774 | 951 |
using (var client = new RestClient(_baseClient)) |
775 | 952 |
{ |
776 |
|
|
953 |
if (!String.IsNullOrWhiteSpace(account)) |
|
954 |
client.BaseAddress = GetAccountUrl(account); |
|
955 |
); |
|
777 | 956 |
client.DeleteWithRetry(container + "/" + objectName, 3); |
778 | 957 |
|
779 | 958 |
var expectedCodes = new[] {HttpStatusCode.NotFound, HttpStatusCode.NoContent}; |
... | ... | |
781 | 960 |
throw CreateWebException("DeleteObject", client.StatusCode); |
782 | 961 |
} |
783 | 962 |
|
784 |
} |
|
963 |
}*/
|
|
785 | 964 |
|
786 |
public void MoveObject(string sourceContainer, string oldObjectName, string targetContainer,string newObjectName)
|
|
965 |
public void MoveObject(string account, string sourceContainer, string oldObjectName, string targetContainer, string newObjectName)
|
|
787 | 966 |
{ |
788 | 967 |
if (String.IsNullOrWhiteSpace(sourceContainer)) |
789 | 968 |
throw new ArgumentNullException("sourceContainer", "The container property can't be empty"); |
... | ... | |
793 | 972 |
throw new ArgumentNullException("targetContainer", "The container property can't be empty"); |
794 | 973 |
if (String.IsNullOrWhiteSpace(newObjectName)) |
795 | 974 |
throw new ArgumentNullException("newObjectName", "The newObjectName property can't be empty"); |
975 |
Contract.EndContractBlock(); |
|
796 | 976 |
|
797 | 977 |
var targetUrl = targetContainer + "/" + newObjectName; |
798 | 978 |
var sourceUrl = String.Format("/{0}/{1}", sourceContainer, oldObjectName); |
799 | 979 |
|
800 | 980 |
using (var client = new RestClient(_baseClient)) |
801 | 981 |
{ |
982 |
if (!String.IsNullOrWhiteSpace(account)) |
|
983 |
client.BaseAddress = GetAccountUrl(account); |
|
984 |
|
|
802 | 985 |
client.Headers.Add("X-Move-From", sourceUrl); |
803 | 986 |
client.PutWithRetry(targetUrl, 3); |
804 | 987 |
|
... | ... | |
808 | 991 |
} |
809 | 992 |
} |
810 | 993 |
|
811 |
public void DeleteObject(string sourceContainer, string objectName, string targetContainer) |
|
994 |
public void DeleteObject(string account, string sourceContainer, string objectName, string targetContainer)
|
|
812 | 995 |
{ |
813 | 996 |
if (String.IsNullOrWhiteSpace(sourceContainer)) |
814 | 997 |
throw new ArgumentNullException("sourceContainer", "The container property can't be empty"); |
... | ... | |
816 | 999 |
throw new ArgumentNullException("objectName", "The oldObjectName property can't be empty"); |
817 | 1000 |
if (String.IsNullOrWhiteSpace(targetContainer)) |
818 | 1001 |
throw new ArgumentNullException("targetContainer", "The container property can't be empty"); |
1002 |
Contract.EndContractBlock(); |
|
819 | 1003 |
|
820 | 1004 |
var targetUrl = targetContainer + "/" + objectName; |
821 | 1005 |
var sourceUrl = String.Format("/{0}/{1}", sourceContainer, objectName); |
822 | 1006 |
|
823 | 1007 |
using (var client = new RestClient(_baseClient)) |
824 | 1008 |
{ |
1009 |
if (!String.IsNullOrWhiteSpace(account)) |
|
1010 |
client.BaseAddress = GetAccountUrl(account); |
|
1011 |
|
|
825 | 1012 |
client.Headers.Add("X-Move-From", sourceUrl); |
826 | 1013 |
client.PutWithRetry(targetUrl, 3); |
827 | 1014 |
|
... | ... | |
839 | 1026 |
|
840 | 1027 |
|
841 | 1028 |
} |
1029 |
|
|
1030 |
public class ShareAccountInfo |
|
1031 |
{ |
|
1032 |
public DateTime? last_modified { get; set; } |
|
1033 |
public string name { get; set; } |
|
1034 |
} |
|
842 | 1035 |
} |
Also available in: Unified diff