LEFT | RIGHT |
1 /* | 1 /* |
2 Copyright 2012 Google Inc | 2 Copyright 2012 Google Inc |
3 | 3 |
4 Licensed under the Apache License, Version 2.0 (the "License"); | 4 Licensed under the Apache License, Version 2.0 (the "License"); |
5 you may not use this file except in compliance with the License. | 5 you may not use this file except in compliance with the License. |
6 You may obtain a copy of the License at | 6 You may obtain a copy of the License at |
7 | 7 |
8 http://www.apache.org/licenses/LICENSE-2.0 | 8 http://www.apache.org/licenses/LICENSE-2.0 |
9 | 9 |
10 Unless required by applicable law or agreed to in writing, software | 10 Unless required by applicable law or agreed to in writing, software |
(...skipping 27 matching lines...) Expand all Loading... |
38 /// See: https://developers.google.com/drive/manage-uploads#resumable for mo
re information on the protocol. | 38 /// See: https://developers.google.com/drive/manage-uploads#resumable for mo
re information on the protocol. |
39 /// </remarks> | 39 /// </remarks> |
40 /// <typeparam name="TRequest"> | 40 /// <typeparam name="TRequest"> |
41 /// The type of the body of this request. Generally this should be the metad
ata related to the content to be· | 41 /// The type of the body of this request. Generally this should be the metad
ata related to the content to be· |
42 /// uploaded. Must be serializable to/from JSON. | 42 /// uploaded. Must be serializable to/from JSON. |
43 /// </typeparam> | 43 /// </typeparam> |
44 public class ResumableUpload<TRequest> | 44 public class ResumableUpload<TRequest> |
45 { | 45 { |
46 #region Constants | 46 #region Constants |
47 | 47 |
48 /// <summary> The class logger. </summary> | 48 /// <summary>The class logger.</summary> |
49 private static readonly ILogger logger = ApplicationContext.Logger.ForTy
pe<ResumableUpload<TRequest>>(); | 49 private static readonly ILogger logger = ApplicationContext.Logger.ForTy
pe<ResumableUpload<TRequest>>(); |
50 | 50 |
51 private const int KB = 0x400; | 51 private const int KB = 0x400; |
52 private const int MB = 0x100000; | 52 private const int MB = 0x100000; |
53 | 53 |
54 /// <summary> Minimum chunk size (except the last one). Default value is
256*KB. </summary> | 54 /// <summary>Minimum chunk size (except the last one). Default value is
256*KB.</summary> |
55 public const int MinimumChunkSize = 256 * KB; | 55 public const int MinimumChunkSize = 256 * KB; |
56 | 56 |
57 /// <summary> Default chunk size. Default value is 10*MB. </summary> | 57 /// <summary>Default chunk size. Default value is 10*MB.</summary> |
58 public const int DefaultChunkSize = 10 * MB; | 58 public const int DefaultChunkSize = 10 * MB; |
59 | 59 |
60 /// <summary> | 60 /// <summary> |
61 /// Defines how many bytes are read from the input stream in each stream
read action.· | 61 /// Defines how many bytes are read from the input stream in each stream
read action.· |
62 /// The read will continue until we read <see cref="MinimumChunkSize"/>
or we reached the end of the stream. | 62 /// The read will continue until we read <see cref="MinimumChunkSize"/>
or we reached the end of the stream. |
63 /// </summary> | 63 /// </summary> |
64 internal int BufferSize = 4 * KB; | 64 internal int BufferSize = 4 * KB; |
65 | 65 |
66 /// <summary> Indicates the stream's size is unknown. </summary> | 66 /// <summary>Indicates the stream's size is unknown.</summary> |
67 private const int UnknownSize = -1; | 67 private const int UnknownSize = -1; |
68 | 68 |
69 /// <summary> The mime type for the encoded JSON body. </summary> | 69 /// <summary>The mime type for the encoded JSON body.</summary> |
70 private const string JsonMimeType = "application/json; charset=UTF-8"; | 70 private const string JsonMimeType = "application/json; charset=UTF-8"; |
71 | 71 |
72 /// <summary> Payload description headers, describing the content itself
. </summary> | 72 /// <summary>Payload description headers, describing the content itself.
</summary> |
73 private const string PayloadContentTypeHeader = "X-Upload-Content-Type"; | 73 private const string PayloadContentTypeHeader = "X-Upload-Content-Type"; |
74 | 74 |
75 /// <summary> Payload description headers, describing the content itself
. </summary> | 75 /// <summary>Payload description headers, describing the content itself.
</summary> |
76 private const string PayloadContentLengthHeader = "X-Upload-Content-Leng
th"; | 76 private const string PayloadContentLengthHeader = "X-Upload-Content-Leng
th"; |
77 | 77 |
78 /// <summary> Specify the type of this upload (this class supports resum
able only). </summary> | 78 /// <summary>Specify the type of this upload (this class supports resuma
ble only).</summary> |
79 private const string UploadType = "uploadType"; | 79 private const string UploadType = "uploadType"; |
80 | 80 |
81 /// <summary> The uploadType parameter value for resumable uploads. </su
mmary> | 81 /// <summary>The uploadType parameter value for resumable uploads.</summ
ary> |
82 private const string Resumable = "resumable"; | 82 private const string Resumable = "resumable"; |
83 | 83 |
84 /// <summary> Content-Range header value for the body upload of zero len
gth files. </summary> | 84 /// <summary>Content-Range header value for the body upload of zero leng
th files.</summary> |
85 private const string ZeroByteContentRangeHeader = "bytes */0"; | 85 private const string ZeroByteContentRangeHeader = "bytes */0"; |
86 | 86 |
87 #endregion // Constants | 87 #endregion // Constants |
88 | 88 |
89 #region Construction | 89 #region Construction |
90 | 90 |
91 /// <summary> | 91 /// <summary> |
92 /// Create a resumable upload instance with the required parameters. | 92 /// Create a resumable upload instance with the required parameters. |
93 /// </summary> | 93 /// </summary> |
94 /// <param name="service">The client service.</param> | 94 /// <param name="service">The client service.</param> |
(...skipping 19 matching lines...) Expand all Loading... |
114 this.Path = path; | 114 this.Path = path; |
115 this.HttpMethod = httpMethod; | 115 this.HttpMethod = httpMethod; |
116 this.ContentStream = contentStream; | 116 this.ContentStream = contentStream; |
117 this.ContentType = contentType; | 117 this.ContentType = contentType; |
118 } | 118 } |
119 | 119 |
120 #endregion // Construction | 120 #endregion // Construction |
121 | 121 |
122 #region Properties | 122 #region Properties |
123 | 123 |
124 /// <summary> Gets or sets the service. </summary> | 124 /// <summary>Gets or sets the service.</summary> |
125 public IClientService Service { get; private set; } | 125 public IClientService Service { get; private set; } |
126 | 126 |
127 /// <summary> | 127 /// <summary> |
128 /// Gets or sets the path of the method (combined with <see cref="IClien
tService.BaseUri"/>) to produce· | 128 /// Gets or sets the path of the method (combined with <see cref="IClien
tService.BaseUri"/>) to produce· |
129 /// absolute Uri.· | 129 /// absolute Uri.· |
130 /// </summary> | 130 /// </summary> |
131 public string Path { get; private set; } | 131 public string Path { get; private set; } |
132 | 132 |
133 /// <summary> Gets or sets the HTTP method of this upload (used to initi
alize the upload). </summary> | 133 /// <summary>Gets or sets the HTTP method of this upload (used to initia
lize the upload).</summary> |
134 public string HttpMethod { get; private set; } | 134 public string HttpMethod { get; private set; } |
135 | 135 |
136 /// <summary> Gets or sets the stream to upload. </summary> | 136 /// <summary>Gets or sets the stream to upload.</summary> |
137 public Stream ContentStream { get; private set; } | 137 public Stream ContentStream { get; private set; } |
138 | 138 |
139 /// <summary> Gets or sets the stream's Content-Type. </summary> | 139 /// <summary>Gets or sets the stream's Content-Type.</summary> |
140 public string ContentType { get; private set; } | 140 public string ContentType { get; private set; } |
141 | 141 |
142 /// <summary> | 142 /// <summary> |
143 /// Gets or sets the length of the steam. Will be <see cref="UnknownSize
" /> if the media content length is· | 143 /// Gets or sets the length of the steam. Will be <see cref="UnknownSize
" /> if the media content length is· |
144 /// unknown.· | 144 /// unknown.· |
145 /// </summary> | 145 /// </summary> |
146 private long StreamLength { get; set; } | 146 private long StreamLength { get; set; } |
147 | 147 |
148 /// <summary> | 148 /// <summary> |
149 /// Gets or sets the content of the last buffer request to the server or
<c>null</c> for none. It is used when· | 149 /// Gets or sets the content of the last buffer request to the server or
<c>null</c> for none. It is used when· |
150 /// the media content length is unknown, for resending it in case of ser
ver error. | 150 /// the media content length is unknown, for resending it in case of ser
ver error. |
151 /// </summary> | 151 /// </summary> |
152 private byte[] LastMediaRequest { get; set; } | 152 private byte[] LastMediaRequest { get; set; } |
153 | 153 |
154 /// <summary> Gets or sets cached byte which indicates if end of stream
has been reached. </summary> | 154 /// <summary>Gets or sets cached byte which indicates if end of stream h
as been reached.</summary> |
155 private byte[] CachedByte { get; set; } | 155 private byte[] CachedByte { get; set; } |
156 | 156 |
157 /// <summary> Gets or sets the last request length. </summary> | 157 /// <summary>Gets or sets the last request length.</summary> |
158 private int LastMediaLength { get; set; } | 158 private int LastMediaLength { get; set; } |
159 | 159 |
160 /// <summary> | 160 /// <summary> |
161 /// Gets or sets the resumable session Uri.· | 161 /// Gets or sets the resumable session Uri.· |
162 /// See https://developers.google.com/drive/manage-uploads#save-session-
uri" for more details. | 162 /// See https://developers.google.com/drive/manage-uploads#save-session-
uri" for more details. |
163 /// </summary> | 163 /// </summary> |
164 private Uri UploadUri { get; set; } | 164 private Uri UploadUri { get; set; } |
165 | 165 |
166 /// <summary> Gets or sets the amount of bytes the server had received s
o far. </summary> | 166 /// <summary>Gets or sets the amount of bytes the server had received so
far.</summary> |
167 private long BytesServerReceived { get; set; } | 167 private long BytesServerReceived { get; set; } |
168 | 168 |
169 /// <summary> Gets or sets the amount of bytes the client had sent so fa
r. </summary> | 169 /// <summary>Gets or sets the amount of bytes the client had sent so far
.</summary> |
170 private long BytesClientSent { get; set; } | 170 private long BytesClientSent { get; set; } |
171 | 171 |
172 /// <summary> Gets or sets the body of this request. </summary> | 172 /// <summary>Gets or sets the body of this request.</summary> |
173 public TRequest Body { get; set; } | 173 public TRequest Body { get; set; } |
174 | 174 |
175 [VisibleForTestOnly] | 175 [VisibleForTestOnly] |
176 internal int chunkSize = DefaultChunkSize; | 176 internal int chunkSize = DefaultChunkSize; |
177 | 177 |
178 /// <summary> | 178 /// <summary> |
179 /// Gets or sets the size of each chunk sent to the server. | 179 /// Gets or sets the size of each chunk sent to the server. |
180 /// Chunks (except the last chunk) must be a multiple of <see cref="Mini
mumChunkSize"/> to be compatible with· | 180 /// Chunks (except the last chunk) must be a multiple of <see cref="Mini
mumChunkSize"/> to be compatible with· |
181 /// Google upload servers. | 181 /// Google upload servers. |
182 /// </summary> | 182 /// </summary> |
183 public int ChunkSize | 183 public int ChunkSize |
184 { | 184 { |
185 get { return chunkSize; } | 185 get { return chunkSize; } |
186 set | 186 set |
187 { | 187 { |
188 if (value < MinimumChunkSize) | 188 if (value < MinimumChunkSize) |
189 { | 189 { |
190 throw new ArgumentOutOfRangeException("ChunkSize"); | 190 throw new ArgumentOutOfRangeException("ChunkSize"); |
191 } | 191 } |
192 chunkSize = value; | 192 chunkSize = value; |
193 } | 193 } |
194 } | 194 } |
195 | 195 |
196 #endregion // Properties | 196 #endregion // Properties |
197 | 197 |
198 #region Events | 198 #region Events |
199 | 199 |
200 /// <summary> Event called whenever the progress of the upload changes.
</summary> | 200 /// <summary>Event called whenever the progress of the upload changes.</
summary> |
201 public event Action<IUploadProgress> ProgressChanged; | 201 public event Action<IUploadProgress> ProgressChanged; |
202 | 202 |
203 #endregion //Events | 203 #endregion //Events |
204 | 204 |
205 #region Error handling (Exception and 5xx) | 205 #region Error handling (Exception and 5xx) |
206 | 206 |
207 /// <summary> | 207 /// <summary> |
208 /// Callback class that is invoked on abnormal response or an exception. | 208 /// Callback class that is invoked on abnormal response or an exception. |
209 /// This class changes the request to query the current status of the up
load in order to find how many bytes·· | 209 /// This class changes the request to query the current status of the up
load in order to find how many bytes·· |
210 /// were successfully uploaded before the error occurred. | 210 /// were successfully uploaded before the error occurred. |
211 /// See https://developers.google.com/drive/manage-uploads#resume-upload
for more details. | 211 /// See https://developers.google.com/drive/manage-uploads#resume-upload
for more details. |
212 /// </summary> | 212 /// </summary> |
213 class ServerErrorCallback : IHttpUnsuccessfulResponseHandler, IHttpExcep
tionHandler, IDisposable | 213 class ServerErrorCallback : IHttpUnsuccessfulResponseHandler, IHttpExcep
tionHandler, IDisposable |
214 { | 214 { |
215 private ResumableUpload<TRequest> Owner { get; set; } | 215 private ResumableUpload<TRequest> Owner { get; set; } |
216 | 216 |
217 /// <summary> | 217 /// <summary> |
218 /// Constructs a new callback and register it as unsuccessful respon
se handler and exception handler on the· | 218 /// Constructs a new callback and register it as unsuccessful respon
se handler and exception handler on the· |
219 /// configurable message handler. | 219 /// configurable message handler. |
220 /// </summary> | 220 /// </summary> |
221 public ServerErrorCallback(ResumableUpload<TRequest> resumable) | 221 public ServerErrorCallback(ResumableUpload<TRequest> resumable) |
222 { | 222 { |
223 this.Owner = resumable; | 223 this.Owner = resumable; |
224 Owner.Service.HttpClient.MessageHandler.UnsuccessfulResponseHand
lers.Add(this); | 224 Owner.Service.HttpClient.MessageHandler.UnsuccessfulResponseHand
lers.Add(this); |
225 Owner.Service.HttpClient.MessageHandler.ExceptionHandlers.Add(th
is); | 225 Owner.Service.HttpClient.MessageHandler.ExceptionHandlers.Add(th
is); |
226 } | 226 } |
227 | 227 |
(...skipping 20 matching lines...) Expand all Loading... |
248 public Task<bool> HandleExceptionAsync(HandleExceptionArgs args) | 248 public Task<bool> HandleExceptionAsync(HandleExceptionArgs args) |
249 { | 249 { |
250 var result = args.SupportsRetry && !args.CancellationToken.IsCan
cellationRequested && | 250 var result = args.SupportsRetry && !args.CancellationToken.IsCan
cellationRequested && |
251 args.Request.RequestUri.Equals(Owner.UploadUri) ? OnServerEr
ror(args.Request) : false; | 251 args.Request.RequestUri.Equals(Owner.UploadUri) ? OnServerEr
ror(args.Request) : false; |
252 | 252 |
253 TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(
); | 253 TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>(
); |
254 tcs.SetResult(result); | 254 tcs.SetResult(result); |
255 return tcs.Task; | 255 return tcs.Task; |
256 } | 256 } |
257 | 257 |
258 /// <summary> Changes the request in order to resume the interrupted
upload. </summary> | 258 /// <summary>Changes the request in order to resume the interrupted
upload.</summary> |
259 private bool OnServerError(HttpRequestMessage request) | 259 private bool OnServerError(HttpRequestMessage request) |
260 { | 260 { |
261 // clear all headers and set Content-Range and Content-Length he
aders | 261 // clear all headers and set Content-Range and Content-Length he
aders |
262 var range = String.Format("bytes */{0}", Owner.StreamLength < 0
? "*" : Owner.StreamLength.ToString()); | 262 var range = String.Format("bytes */{0}", Owner.StreamLength < 0
? "*" : Owner.StreamLength.ToString()); |
263 request.Headers.Clear(); | 263 request.Headers.Clear(); |
264 request.Method = System.Net.Http.HttpMethod.Put; | 264 request.Method = System.Net.Http.HttpMethod.Put; |
265 request.SetEmptyContent().Headers.Add("Content-Range", range); | 265 request.SetEmptyContent().Headers.Add("Content-Range", range); |
266 return true; | 266 return true; |
267 } | 267 } |
268 | 268 |
269 public void Dispose() | 269 public void Dispose() |
270 { | 270 { |
271 Owner.Service.HttpClient.MessageHandler.UnsuccessfulResponseHand
lers.Remove(this); | 271 Owner.Service.HttpClient.MessageHandler.UnsuccessfulResponseHand
lers.Remove(this); |
272 Owner.Service.HttpClient.MessageHandler.ExceptionHandlers.Remove
(this); | 272 Owner.Service.HttpClient.MessageHandler.ExceptionHandlers.Remove
(this); |
273 } | 273 } |
274 } | 274 } |
275 | 275 |
276 #endregion | 276 #endregion |
277 | 277 |
278 #region Progress Monitoring | 278 #region Progress Monitoring |
279 | 279 |
280 /// <summary> Class that communicates the progress of resumable uploads
to a container. </summary> | 280 /// <summary>Class that communicates the progress of resumable uploads t
o a container.</summary> |
281 private class ResumableUploadProgress : IUploadProgress | 281 private class ResumableUploadProgress : IUploadProgress |
282 { | 282 { |
283 /// <summary> | 283 /// <summary> |
284 /// Create a ResumableUploadProgress instance. | 284 /// Create a ResumableUploadProgress instance. |
285 /// </summary> | 285 /// </summary> |
286 /// <param name="status">The status of the upload.</param> | 286 /// <param name="status">The status of the upload.</param> |
287 /// <param name="bytesSent">The number of bytes sent so far.</param> | 287 /// <param name="bytesSent">The number of bytes sent so far.</param> |
288 public ResumableUploadProgress(UploadStatus status, long bytesSent) | 288 public ResumableUploadProgress(UploadStatus status, long bytesSent) |
289 { | 289 { |
290 Status = status; | 290 Status = status; |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
376 } | 376 } |
377 catch (Exception ex) | 377 catch (Exception ex) |
378 { | 378 { |
379 logger.Error(ex, "MediaUpload[{0}] - Exception occurred while up
loading media", UploadUri); | 379 logger.Error(ex, "MediaUpload[{0}] - Exception occurred while up
loading media", UploadUri); |
380 UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceiv
ed)); | 380 UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceiv
ed)); |
381 } | 381 } |
382 | 382 |
383 return Progress; | 383 return Progress; |
384 } | 384 } |
385 | 385 |
386 /// <summary> Uploads the content asynchronously to the server.</summary
> | 386 /// <summary>Uploads the content asynchronously to the server.</summary> |
387 /// <remarks> | 387 /// <remarks> |
388 /// In case the upload fails the task will not be completed. In that cas
e the task's· | 388 /// In case the upload fails the task will not be completed. In that cas
e the task's· |
389 /// <seealso cref="System.Threading.Tasks.Task.Exception"/> property wil
l bet set, or its· | 389 /// <seealso cref="System.Threading.Tasks.Task.Exception"/> property wil
l bet set, or its· |
390 /// <seealso cref="System.Threading.Tasks.Task.IsCanceled"/> property wi
ll be true. | 390 /// <seealso cref="System.Threading.Tasks.Task.IsCanceled"/> property wi
ll be true. |
391 /// </remarks> | 391 /// </remarks> |
392 public Task<IUploadProgress> UploadAsync() | 392 public Task<IUploadProgress> UploadAsync() |
393 { | 393 { |
394 return UploadAsync(CancellationToken.None); | 394 return UploadAsync(CancellationToken.None); |
395 } | 395 } |
396 | 396 |
397 /// <summary> Uploads the content asynchronously to the server.</summary
> | 397 /// <summary>Uploads the content asynchronously to the server.</summary> |
398 /// <param name="cancellationToken">The cancellation token to cancel a r
equest in the middle.</param> | 398 /// <param name="cancellationToken">The cancellation token to cancel a r
equest in the middle.</param> |
399 public Task<IUploadProgress> UploadAsync(CancellationToken cancellationT
oken) | 399 public Task<IUploadProgress> UploadAsync(CancellationToken cancellationT
oken) |
400 { | 400 { |
401 TaskCompletionSource<IUploadProgress> tcs = new TaskCompletionSource
<IUploadProgress>(); | 401 TaskCompletionSource<IUploadProgress> tcs = new TaskCompletionSource
<IUploadProgress>(); |
402 Task.Factory.StartNew(async () => | 402 Task.Factory.StartNew(async () => |
403 { | 403 { |
404 try | 404 try |
405 { | 405 { |
406 var response = await Upload(cancellationToken).ConfigureAwai
t(false); | 406 var response = await Upload(cancellationToken).ConfigureAwai
t(false); |
407 if (response.Exception != null) | 407 if (response.Exception != null) |
(...skipping 29 matching lines...) Expand all Loading... |
437 } | 437 } |
438 | 438 |
439 /// <summary> | 439 /// <summary> |
440 /// Process a response from the final upload chunk call. | 440 /// Process a response from the final upload chunk call. |
441 /// </summary> | 441 /// </summary> |
442 /// <param name="httpResponse">The response body from the final uploaded
chunk.</param> | 442 /// <param name="httpResponse">The response body from the final uploaded
chunk.</param> |
443 protected virtual void ProcessResponse(HttpResponseMessage httpResponse) | 443 protected virtual void ProcessResponse(HttpResponseMessage httpResponse) |
444 { | 444 { |
445 } | 445 } |
446 | 446 |
447 /// <summary> | 447 /// <summary> |
448 /// Uploads the next chunk of data to the server. | 448 /// Uploads the next chunk of data to the server. |
449 /// </summary> | 449 /// </summary> |
450 /// <returns>· | 450 /// <returns>· |
451 /// <c>True</c> if the entire media has been completely uploaded. | 451 /// <c>True</c> if the entire media has been completely uploaded. |
452 /// </returns> | 452 /// </returns> |
453 protected async Task<bool> SendNextChunk(Stream stream, CancellationToke
n cancellationToken) | 453 protected async Task<bool> SendNextChunk(Stream stream, CancellationToke
n cancellationToken) |
454 { | 454 { |
455 cancellationToken.ThrowIfCancellationRequested(); | 455 cancellationToken.ThrowIfCancellationRequested(); |
456 | 456 |
457 HttpRequestMessage request = new RequestBuilder() | 457 HttpRequestMessage request = new RequestBuilder() |
(...skipping 28 matching lines...) Expand all Loading... |
486 // The upload protocol uses 308 to indicate that there is more d
ata expected from the server. | 486 // The upload protocol uses 308 to indicate that there is more d
ata expected from the server. |
487 BytesServerReceived = GetNextByte(response.Headers.GetValues("Ra
nge").First()); | 487 BytesServerReceived = GetNextByte(response.Headers.GetValues("Ra
nge").First()); |
488 logger.Debug("MediaUpload[{0}] - {1} Bytes were sent successfull
y", UploadUri, BytesServerReceived); | 488 logger.Debug("MediaUpload[{0}] - {1} Bytes were sent successfull
y", UploadUri, BytesServerReceived); |
489 return false; | 489 return false; |
490 } | 490 } |
491 | 491 |
492 var error = await Service.DeserializeError(response).ConfigureAwait(
false); | 492 var error = await Service.DeserializeError(response).ConfigureAwait(
false); |
493 throw new GoogleApiException(Service.Name, error.ToString()); | 493 throw new GoogleApiException(Service.Name, error.ToString()); |
494 } | 494 } |
495 | 495 |
496 /// <summary> A callback when the media was uploaded successfully. </sum
mary> | 496 /// <summary>A callback when the media was uploaded successfully.</summa
ry> |
497 private void MediaCompleted(HttpResponseMessage response) | 497 private void MediaCompleted(HttpResponseMessage response) |
498 { | 498 { |
499 logger.Debug("MediaUpload[{0}] - media was uploaded successfully", U
ploadUri); | 499 logger.Debug("MediaUpload[{0}] - media was uploaded successfully", U
ploadUri); |
500 ProcessResponse(response); | 500 ProcessResponse(response); |
501 BytesServerReceived = LastMediaLength; | 501 BytesServerReceived = LastMediaLength; |
502 | 502 |
503 // clear the last request byte array | 503 // clear the last request byte array |
504 LastMediaRequest = null; | 504 LastMediaRequest = null; |
505 } | 505 } |
506 | 506 |
507 /// <summary> Prepares the given request with the next chunk in case the
steam length is unknown. </summary> | 507 /// <summary>Prepares the given request with the next chunk in case the
steam length is unknown.</summary> |
508 private void PrepareNextChunkUnknownSize(HttpRequestMessage request, Str
eam stream, | 508 private void PrepareNextChunkUnknownSize(HttpRequestMessage request, Str
eam stream, |
509 CancellationToken cancellationToken) | 509 CancellationToken cancellationToken) |
510 { | 510 { |
511 // We save the current request, so we would be able to resend those
bytes in case of a server error | 511 // We save the current request, so we would be able to resend those
bytes in case of a server error |
512 if (LastMediaRequest == null) | 512 if (LastMediaRequest == null) |
513 { | 513 { |
514 LastMediaRequest = new byte[ChunkSize]; | 514 LastMediaRequest = new byte[ChunkSize]; |
515 } | 515 } |
516 | 516 |
517 LastMediaLength = 0; | 517 LastMediaLength = 0; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
565 CachedByte = null; | 565 CachedByte = null; |
566 } | 566 } |
567 } | 567 } |
568 | 568 |
569 // set Content-Length and Content-Range | 569 // set Content-Length and Content-Range |
570 var byteArrayContent = new ByteArrayContent(LastMediaRequest, 0, Las
tMediaLength); | 570 var byteArrayContent = new ByteArrayContent(LastMediaRequest, 0, Las
tMediaLength); |
571 byteArrayContent.Headers.Add("Content-Range", GetContentRangeHeader(
BytesServerReceived, LastMediaLength)); | 571 byteArrayContent.Headers.Add("Content-Range", GetContentRangeHeader(
BytesServerReceived, LastMediaLength)); |
572 request.Content = byteArrayContent; | 572 request.Content = byteArrayContent; |
573 } | 573 } |
574 | 574 |
575 /// <summary> Prepares the given request with the next chunk in case the
steam length is known. </summary> | 575 /// <summary>Prepares the given request with the next chunk in case the
steam length is known.</summary> |
576 private void PrepareNextChunkKnownSize(HttpRequestMessage request, Strea
m stream, | 576 private void PrepareNextChunkKnownSize(HttpRequestMessage request, Strea
m stream, |
577 CancellationToken cancellationToken) | 577 CancellationToken cancellationToken) |
578 { | 578 { |
579 int chunkSize = (int)Math.Min(StreamLength - BytesServerReceived, (l
ong)ChunkSize); | 579 int chunkSize = (int)Math.Min(StreamLength - BytesServerReceived, (l
ong)ChunkSize); |
580 | 580 |
581 // stream length is known and it supports seek and position operatio
ns. | 581 // stream length is known and it supports seek and position operatio
ns. |
582 // We can change the stream position and read bytes from the last po
int | 582 // We can change the stream position and read bytes from the last po
int |
583 byte[] buffer = new byte[Math.Min(chunkSize, BufferSize)]; | 583 byte[] buffer = new byte[Math.Min(chunkSize, BufferSize)]; |
584 | 584 |
585 // if the number of bytes received by the server isn't equal to the
amount of bytes the client sent, we· | 585 // if the number of bytes received by the server isn't equal to the
amount of bytes the client sent, we· |
(...skipping 18 matching lines...) Expand all Loading... |
604 } | 604 } |
605 | 605 |
606 // set the stream position to beginning and wrap it with stream cont
ent | 606 // set the stream position to beginning and wrap it with stream cont
ent |
607 ms.Position = 0; | 607 ms.Position = 0; |
608 request.Content = new StreamContent(ms); | 608 request.Content = new StreamContent(ms); |
609 request.Content.Headers.Add("Content-Range", GetContentRangeHeader(B
ytesServerReceived, chunkSize)); | 609 request.Content.Headers.Add("Content-Range", GetContentRangeHeader(B
ytesServerReceived, chunkSize)); |
610 | 610 |
611 LastMediaLength = chunkSize; | 611 LastMediaLength = chunkSize; |
612 } | 612 } |
613 | 613 |
614 /// <summary> Returns the next byte index need to be sent. </summary> | 614 /// <summary>Returns the next byte index need to be sent.</summary> |
615 private long GetNextByte(string range) | 615 private long GetNextByte(string range) |
616 { | 616 { |
617 return long.Parse(range.Substring(range.IndexOf('-') 1)) 1; | 617 return long.Parse(range.Substring(range.IndexOf('-') 1)) 1; |
618 } | 618 } |
619 | 619 |
620 /// <summary> | 620 /// <summary> |
621 /// Build a content range header of the form: "bytes X-Y/T" where: | 621 /// Build a content range header of the form: "bytes X-Y/T" where: |
622 /// <list type=""> | 622 /// <list type=""> |
623 /// <item>X is the first byte being sent.</item> | 623 /// <item>X is the first byte being sent.</item> |
624 /// <item>Y is the last byte in the range being sent (inclusive).</item> | 624 /// <item>Y is the last byte in the range being sent (inclusive).</item> |
(...skipping 18 matching lines...) Expand all Loading... |
643 { | 643 { |
644 return ZeroByteContentRangeHeader; | 644 return ZeroByteContentRangeHeader; |
645 } | 645 } |
646 else | 646 else |
647 { | 647 { |
648 long chunkEnd = chunkStart chunkSize - 1; | 648 long chunkEnd = chunkStart chunkSize - 1; |
649 return String.Format("bytes {0}-{1}/{2}", chunkStart, chunkEnd,
strLength); | 649 return String.Format("bytes {0}-{1}/{2}", chunkStart, chunkEnd,
strLength); |
650 } | 650 } |
651 } | 651 } |
652 | 652 |
653 /// <summary> Creates a request to initialize a request. </summary> | 653 /// <summary>Creates a request to initialize a request.</summary> |
654 private HttpRequestMessage CreateInitializeRequest() | 654 private HttpRequestMessage CreateInitializeRequest() |
655 { | 655 { |
656 var builder = new RequestBuilder() | 656 var builder = new RequestBuilder() |
657 { | 657 { |
658 BaseUri = new Uri(Service.BaseUri), | 658 BaseUri = new Uri(Service.BaseUri), |
659 Path = Path, | 659 Path = Path, |
660 Method = HttpMethod, | 660 Method = HttpMethod, |
661 }; | 661 }; |
662 | 662 |
663 // init parameters | 663 // init parameters |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
753 /// <remarks> | 753 /// <remarks> |
754 /// This property will be set during upload. The <see cref="ResponseRece
ived"/> event | 754 /// This property will be set during upload. The <see cref="ResponseRece
ived"/> event |
755 /// is triggered when this has been set. | 755 /// is triggered when this has been set. |
756 /// </remarks> | 756 /// </remarks> |
757 public TResponse ResponseBody { get; private set; } | 757 public TResponse ResponseBody { get; private set; } |
758 | 758 |
759 #endregion // Properties | 759 #endregion // Properties |
760 | 760 |
761 #region Events | 761 #region Events |
762 | 762 |
763 /// <summary> Event which is called when the response metadata is proces
sed. </summary> | 763 /// <summary>Event which is called when the response metadata is process
ed.</summary> |
764 public event Action<TResponse> ResponseReceived; | 764 public event Action<TResponse> ResponseReceived; |
765 | 765 |
766 #endregion // Events | 766 #endregion // Events |
767 | 767 |
768 #region Overrides | 768 #region Overrides |
769 | 769 |
770 /// <summary> Process the response body </summary> | 770 /// <summary>Process the response body </summary> |
771 protected override void ProcessResponse(HttpResponseMessage response) | 771 protected override void ProcessResponse(HttpResponseMessage response) |
772 { | 772 { |
773 base.ProcessResponse(response); | 773 base.ProcessResponse(response); |
774 ResponseBody = Service.DeserializeResponse<TResponse>(response).Resu
lt; | 774 ResponseBody = Service.DeserializeResponse<TResponse>(response).Resu
lt; |
775 | 775 |
776 if (ResponseReceived != null) | 776 if (ResponseReceived != null) |
777 ResponseReceived(ResponseBody); | 777 ResponseReceived(ResponseBody); |
778 } | 778 } |
779 | 779 |
780 #endregion // Overrides | 780 #endregion // Overrides |
781 } | 781 } |
782 } | 782 } |
LEFT | RIGHT |