/// <summary>Creates a request to initialize a request.</summary> private HttpRequestMessage CreateInitializeRequest() { var builder = new RequestBuilder() { BaseUri = new Uri(Service.BaseUri), Path = Path, Method = HttpMethod, }; // init parameters builder.AddParameter(RequestParameterType.Query, "key", Service.ApiKey); builder.AddParameter(RequestParameterType.Query, "uploadType", "resumable"); SetAllPropertyValues(builder); HttpRequestMessage request = builder.CreateRequest(); if (ContentType != null) { request.Headers.Add(PayloadContentTypeHeader, ContentType); } // if the length is unknown at the time of this request, omit "X-Upload-Content-Length" header if (StreamLength != UnknownSize) { request.Headers.Add(PayloadContentLengthHeader, StreamLength.ToString()); } Service.SetRequestSerailizedContent(request, Body); return(request); }
/// <summary>Asynchronously resumes the upload form the last point it was interrupted.</summary> /// <param name="cancellationToken">A cancellation token to cancel operation.</param> public async Task <IUploadProgress> ResumeAsync(CancellationToken cancellationToken) { if (UploadUri == null) { Logger.Info("There isn't any upload in progress, so starting to upload again"); return(await UploadAsync(cancellationToken).ConfigureAwait(false)); } // The first "resuming" request is to query the server in which point the upload was interrupted. var range = String.Format("bytes */{0}", StreamLength < 0 ? "*" : StreamLength.ToString()); HttpRequestMessage request = new RequestBuilder() { BaseUri = UploadUri, Method = HttpConsts.Put }.CreateRequest(); request.SetEmptyContent().Headers.Add("Content-Range", range); try { HttpResponseMessage response; using (var callback = new ServerErrorCallback(this)) { response = await Service.HttpClient.SendAsync(request, cancellationToken) .ConfigureAwait(false); } if (await HandleResponse(response).ConfigureAwait(false)) { // All the media was successfully upload. UpdateProgress(new ResumableUploadProgress(UploadStatus.Completed, BytesServerReceived)); return(Progress); } } catch (TaskCanceledException ex) { Logger.Error(ex, "MediaUpload[{0}] - Task was canceled", UploadUri); UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); throw ex; } catch (Exception ex) { Logger.Error(ex, "MediaUpload[{0}] - Exception occurred while resuming uploading media", UploadUri); UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); return(Progress); } // Continue to upload the media stream. return(await UploadCoreAsync(cancellationToken).ConfigureAwait(false)); }
/// <summary> /// Build a content range header of the form: "bytes X-Y/T" where: /// <list type=""> /// <item>X is the first byte being sent.</item> /// <item>Y is the last byte in the range being sent (inclusive).</item> /// <item>T is the total number of bytes in the range or * for unknown size.</item> /// </list> /// </summary> /// <remarks> /// See: RFC2616 HTTP/1.1, Section 14.16 Header Field Definitions, Content-Range /// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16 /// </remarks> /// <param name="chunkStart">Start of the chunk.</param> /// <param name="chunkSize">Size of the chunk being sent.</param> /// <returns>The content range header value.</returns> private string GetContentRangeHeader(long chunkStart, long chunkSize) { string strLength = StreamLength < 0 ? "*" : StreamLength.ToString(); // If a file of length 0 is sent, one chunk needs to be sent with 0 size. // This chunk cannot be specified with the standard (inclusive) range header. // In this case, use * to indicate no bytes sent in the Content-Range header. if (chunkStart == 0 && chunkSize == 0 && StreamLength == 0) { return(ZeroByteContentRangeHeader); } else { long chunkEnd = chunkStart + chunkSize - 1; return(String.Format("bytes {0}-{1}/{2}", chunkStart, chunkEnd, strLength)); } }
protected override Task <HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) { var response = new HttpResponseMessage(); var range = string.Empty; switch (Calls) { case 1: if (PathParameters == null) { Assert.That(request.RequestUri.AbsolutePath, Is.EqualTo("/")); } else { Assert.That(request.RequestUri.AbsolutePath, Is.EqualTo("/" + PathParameters)); } Assert.That(request.RequestUri.Query, Is.EqualTo("?uploadType=resumable" + QueryParameters)); Assert.That(request.Headers.GetValues("X-Upload-Content-Type").First(), Is.EqualTo("text/plain")); Assert.That(request.Headers.GetValues("X-Upload-Content-Length").First(), Is.EqualTo(StreamLength.ToString())); response.Headers.Location = uploadUri; break; case 2: Assert.That(request.RequestUri, Is.EqualTo(uploadUri)); range = String.Format("bytes 0-{0}/{1}", StreamLength - 1, StreamLength); Assert.That(request.Content.Headers.GetValues("Content-Range").First(), Is.EqualTo(range)); Assert.That(request.Content.Headers.ContentLength, Is.EqualTo(StreamLength)); break; } TaskCompletionSource <HttpResponseMessage> tcs = new TaskCompletionSource <HttpResponseMessage>(); tcs.SetResult(response); return(tcs.Task); }
/// <summary> /// Asynchronously resumes the upload from the last point it was interrupted. /// Use when the program was restarted and you wish to resume the upload that was in progress when the program was halted. /// Implemented only for ContentStreams where .CanSeek is True. /// </summary> /// <remarks> /// In your application's UploadSessionData Event Handler, store UploadUri.AbsoluteUri property value (resumable session URI string value) /// to persistent storage for use with Resume() or ResumeAsync() upon a program restart. /// It is strongly recommended that the FullPathFilename of the media file that is being uploaded is saved also so that a subsequent execution of the /// program can compare the saved FullPathFilename value to the FullPathFilename of the media file that it has opened for uploading. /// You do not need to seek to restart point in the ContentStream file. /// </remarks> /// <param name="uploadUri">VideosResource.InsertMediaUpload UploadUri property value that was saved to persistent storage during a prior execution.</param> /// <param name="cancellationToken">A cancellation token to cancel the asynchronous operation.</param> public async Task <IUploadProgress> ResumeAsync(Uri uploadUri, CancellationToken cancellationToken) { // When called with uploadUri parameter of non-null value, the UploadUri is being // provided upon a program restart to resume a previously interrupted upload. if (uploadUri != null) { if (ContentStream.CanSeek) { Logger.Info("Resuming after program restart: UploadUri={0}", uploadUri); UploadUri = uploadUri; StreamLength = ContentStream.Length; } else { throw new NotImplementedException("Resume after program restart not allowed when ContentStream.CanSeek is false"); } } if (UploadUri == null) { Logger.Info("There isn't any upload in progress, so starting to upload again"); return(await UploadAsync(cancellationToken).ConfigureAwait(false)); } // The first "resuming" request is to query the server in which point the upload was interrupted. var range = String.Format("bytes */{0}", StreamLength < 0 ? "*" : StreamLength.ToString()); HttpRequestMessage request = new RequestBuilder() { BaseUri = UploadUri, Method = HttpConsts.Put }.CreateRequest(); request.SetEmptyContent().Headers.Add("Content-Range", range); try { HttpResponseMessage response; using (var callback = new ServerErrorCallback(this)) { response = await HttpClient.SendAsync(request, cancellationToken) .ConfigureAwait(false); } if (await HandleResponse(response).ConfigureAwait(false)) { // All the media was successfully upload. UpdateProgress(new ResumableUploadProgress(UploadStatus.Completed, BytesServerReceived)); return(Progress); } } catch (TaskCanceledException ex) { Logger.Error(ex, "MediaUpload[{0}] - Task was canceled", UploadUri); UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); throw ex; } catch (Exception ex) { Logger.Error(ex, "MediaUpload[{0}] - Exception occurred while resuming uploading media", UploadUri); UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); return(Progress); } // Continue to upload the media stream. return(await UploadCoreAsync(cancellationToken).ConfigureAwait(false)); }