/// <summary> /// Get the status of the session. Stores returned session internally. /// Updates internal list of ranges remaining to be uploaded (according to the server). /// </summary> /// <returns>UploadSession returned by the server.</returns> public virtual async Task <UploadSession> UpdateSessionStatusAsync() { var request = new UploadSessionRequest(this.Session, this.client, null); var newSession = await request.GetAsync().ConfigureAwait(false); var newRangesRemaining = this.GetRangesRemaining(newSession); this.rangesRemaining = newRangesRemaining; newSession.UploadUrl = this.Session.UploadUrl; // Sometimes the UploadUrl is not returned this.Session = newSession; return(newSession); }
// Uploads a large file to the current user's root directory. private static void UploadLargeFile(GraphServiceClient graphClient, Stream fileStream, string fileName) { // Create the upload session. The access token is no longer required as you have session established for the upload. // POST /v1.0/drive/root:/UploadLargeFile.bmp:/microsoft.graph.createUploadSession Microsoft.Graph.UploadSession uploadSession = graphClient.Me.Drive.Root.ItemWithPath(fileName).CreateUploadSession().Request().PostAsync().Result; int maxChunkSize = 320 * 1024; // 320 KB - Change this to your chunk size. 5MB is the default. ChunkedUploadProvider provider = new ChunkedUploadProvider(uploadSession, graphClient, fileStream, maxChunkSize); //Replace own implementation in favour of UploadAsync-Helper (https://github.com/OneDrive/onedrive-sdk-csharp/blob/master/docs/chunked-uploads.md) _ = provider.UploadAsync().Result; }
internal List <Tuple <long, long> > GetRangesRemaining(UploadSession session) { // nextExpectedRanges: https://dev.onedrive.com/items/upload_large_files.htm // Sample: ["12345-55232","77829-99375"] // Also, second number in range can be blank, which means 'until the end' var newRangesRemaining = new List <Tuple <long, long> >(); foreach (var range in session.NextExpectedRanges) { var rangeSpecifiers = range.Split('-'); newRangesRemaining.Add(new Tuple <long, long>(long.Parse(rangeSpecifiers[0]), string.IsNullOrEmpty(rangeSpecifiers[1]) ? this.totalUploadLength - 1 : long.Parse(rangeSpecifiers[1]))); } return(newRangesRemaining); }
/// <summary> /// Helps with resumable uploads. Generates chunk requests based on <paramref name="session"/> /// information, and can control uploading of requests using <paramref name="client"/> /// </summary> /// <param name="session">Session information.</param> /// <param name="client">Client used to upload chunks.</param> /// <param name="uploadStream">Readable, seekable stream to be uploaded. Length of session is determined via uploadStream.Length</param> /// <param name="maxChunkSize">Max size of each chunk to be uploaded. Multiple of 320 KiB (320 * 1024) is required. /// If less than 0, default value of 5 MiB is used. .</param> public ChunkedUploadProvider(UploadSession session, IBaseClient client, Stream uploadStream, int maxChunkSize = -1) { if (!uploadStream.CanRead || !uploadStream.CanSeek) { throw new ArgumentException("Must provide stream that can read and seek"); } this.Session = session; this.client = client; this.uploadStream = uploadStream; this.rangesRemaining = this.GetRangesRemaining(session); this.maxChunkSize = maxChunkSize < 0 ? DefaultMaxChunkSize : maxChunkSize; if (this.maxChunkSize % RequiredChunkSizeIncrement != 0) { throw new ArgumentException("Max chunk size must be a multiple of 320 KiB", nameof(maxChunkSize)); } }
/// <summary> /// Process raw HTTP response from Upload request /// </summary> /// <typeparam name="T">The type to return</typeparam> /// <param name="response">The HttpResponseMessage to handle.</param> /// <returns></returns> public async Task <UploadResult <T> > HandleResponse <T>(HttpResponseMessage response) { if (response.Content == null) { throw new ServiceException(new Error { Code = ErrorConstants.Codes.GeneralException, Message = ErrorConstants.Messages.NoResponseForUpload }); } // Give back the info from the server for ongoing upload as the upload is ongoing using (Stream responseSteam = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { try { if (!response.IsSuccessStatusCode) { ErrorResponse errorResponse = this._serializer.DeserializeObject <ErrorResponse>(responseSteam); Error error = errorResponse.Error; string rawResponseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); // Throw exception to know something went wrong. throw new ServiceException(error, response.Headers, response.StatusCode, rawResponseBody); } var uploadResult = new UploadResult <T>(); /* * Check if we have a status code 201 to know if the upload completed successfully. * This will be returned when uploading a FileAttachment with a location header but empty response hence * This could also be returned when uploading a DriveItem with an ItemResponse but no location header. */ if (response.StatusCode == HttpStatusCode.Created) { uploadResult.ItemResponse = this._serializer.DeserializeObject <T>(responseSteam); uploadResult.Location = response.Headers.Location; } else { /* * The response could be either a 200 or a 202 response. * DriveItem Upload returns the upload session in a 202 response while FileAttachment in a 200 response * However, successful upload completion for a DriveItem the response could also come in a 200 response and * hence we validate this by checking the NextExpectedRanges parameter which is present in an ongoing upload */ UploadSession uploadSession = this._serializer.DeserializeObject <UploadSession>(responseSteam); if (uploadSession?.NextExpectedRanges != null) { uploadResult.UploadSession = uploadSession; } else { //Upload is most likely done as DriveItem info may come in a 200 response responseSteam.Position = 0; //reset uploadResult.ItemResponse = this._serializer.DeserializeObject <T>(responseSteam); } } return(uploadResult); } catch (JsonSerializationException exception) { string rawResponseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false); throw new ServiceException(new Error() { Code = ErrorConstants.Codes.GeneralException, Message = ErrorConstants.Messages.UnableToDeserializeContent, }, response.Headers, response.StatusCode, rawResponseBody, exception); } } }
public UploadSessionRequest(UploadSession session, IBaseClient client, IEnumerable <Option> options) : base(session.UploadUrl, client, options) { this.session = session; }