Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions src/Microsoft.Graph/Requests/Helpers/ChunkedUploadProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ public async Task<UploadSession> DeleteSession()
public async Task<DriveItem> UploadAsync(int maxTries = 3, IEnumerable<Option> options = null)
{
var uploadTries = 0;
var readBuffer = new byte[this.maxChunkSize];
var trackedExceptions = new List<Exception>();

while (uploadTries < maxTries)
Expand All @@ -134,7 +133,7 @@ public async Task<DriveItem> UploadAsync(int maxTries = 3, IEnumerable<Option> o

foreach (var request in chunkRequests)
{
var result = await this.GetChunkRequestResponseAsync(request, readBuffer, trackedExceptions).ConfigureAwait(false);
var result = await this.GetChunkRequestResponseAsync(request, trackedExceptions).ConfigureAwait(false);

if (result.UploadSucceeded)
{
Expand All @@ -147,7 +146,7 @@ public async Task<DriveItem> UploadAsync(int maxTries = 3, IEnumerable<Option> o
if (uploadTries < maxTries)
{
// Exponential backoff in case of failures.
await System.Threading.Tasks.Task.Delay(2000 * uploadTries * uploadTries).ConfigureAwait(false);
await Task.Delay(2000 * uploadTries * uploadTries).ConfigureAwait(false);
}
}

Expand All @@ -156,24 +155,34 @@ public async Task<DriveItem> UploadAsync(int maxTries = 3, IEnumerable<Option> o

/// <summary>
/// Write a chunk of data using the UploadChunkRequest.
/// This overload is obsolete and is kept for backward compatibility
/// </summary>
/// <param name="request">The UploadChunkRequest to make the request with.</param>
/// <param name="readBuffer">The byte[] content to read from.</param>
/// <param name="exceptionTrackingList">A list of exceptions to use to track progress. ChunkedUpload may retry.</param>
/// <returns></returns>
[Obsolete("This overload is obsolete. The readBuffer parameter is no longer used. Please use the other overload.")]
public virtual async Task<UploadChunkResult> GetChunkRequestResponseAsync(UploadChunkRequest request, byte[] readBuffer, ICollection<Exception> exceptionTrackingList)
{
return await this.GetChunkRequestResponseAsync(request, exceptionTrackingList);
}


/// <summary>
/// Write a chunk of data using the UploadChunkRequest.
/// </summary>
/// <param name="request">The UploadChunkRequest to make the request with.</param>
/// <param name="exceptionTrackingList">A list of exceptions to use to track progress. ChunkedUpload may retry.</param>
/// <returns></returns>
public virtual async Task<UploadChunkResult> GetChunkRequestResponseAsync(UploadChunkRequest request, ICollection<Exception> exceptionTrackingList)
{
var firstAttempt = true;
this.uploadStream.Seek(request.RangeBegin, SeekOrigin.Begin);
await this.uploadStream.ReadAsync(readBuffer, 0, request.RangeLength).ConfigureAwait(false);

while (true)
{
using (var requestBodyStream = new MemoryStream(request.RangeLength))
using (var requestBodyStream = new ReadOnlySubStream(this.uploadStream,request.RangeBegin,request.RangeLength))
{
await requestBodyStream.WriteAsync(readBuffer, 0, request.RangeLength).ConfigureAwait(false);
requestBodyStream.Seek(0, SeekOrigin.Begin);

try
{
return await request.PutAsync(requestBodyStream).ConfigureAwait(false);
Expand Down
132 changes: 132 additions & 0 deletions src/Microsoft.Graph/Requests/Helpers/ReadOnlySubStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// ------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ---------------

namespace Microsoft.Graph
{
using System;
using System.Diagnostics;
using System.IO;

internal class ReadOnlySubStream:Stream
{
private readonly long startInSuperStream;
private long positionInSuperStream;
private readonly long endInSuperStream;
private readonly Stream superStream;
private bool canRead;
private bool isDisposed;

public ReadOnlySubStream(Stream superStream, long startPosition, long maxLength)
{
this.startInSuperStream = startPosition;
this.positionInSuperStream = startPosition;
this.endInSuperStream = startPosition + maxLength;
this.superStream = superStream;
this.canRead = true;
this.isDisposed = false;
}

public override long Length
{
get
{
ThrowIfDisposed();

return endInSuperStream - startInSuperStream;
}
}

public override long Position
{
get
{
ThrowIfDisposed();

return positionInSuperStream - startInSuperStream;
}
set
{
ThrowIfDisposed();

throw new NotSupportedException("seek not support");
}
}

public override bool CanRead => superStream.CanRead && canRead;

public override bool CanSeek => false;

public override bool CanWrite => false;

private void ThrowIfDisposed()
{
if (isDisposed)
throw new ObjectDisposedException(GetType().ToString(), nameof(this.superStream));
}

private void ThrowIfCantRead()
{
if (!CanRead)
throw new NotSupportedException("read not support");
}

public override int Read(byte[] buffer, int offset, int count)
{
// parameter validation sent to _superStream.Read
int origCount = count;

ThrowIfDisposed();
ThrowIfCantRead();

if (superStream.Position != positionInSuperStream)
superStream.Seek(positionInSuperStream, SeekOrigin.Begin);
if (positionInSuperStream + count > endInSuperStream)
count = (int)(endInSuperStream - positionInSuperStream);

Debug.Assert(count >= 0);
Debug.Assert(count <= origCount);

int ret = superStream.Read(buffer, offset, count);

positionInSuperStream += ret;
return ret;
}

public override long Seek(long offset, SeekOrigin origin)
{
ThrowIfDisposed();
throw new NotSupportedException("seek not support");
}

public override void SetLength(long value)
{
ThrowIfDisposed();
throw new NotSupportedException("seek and write not support");
}

public override void Write(byte[] buffer, int offset, int count)
{
ThrowIfDisposed();
throw new NotSupportedException("write not support");
}

public override void Flush()
{
ThrowIfDisposed();
throw new NotSupportedException("write not support");
}

// Close the stream for reading. Note that this does NOT close the superStream (since
// the substream is just 'a chunk' of the super-stream
protected override void Dispose(bool disposing)
{
if (disposing && !isDisposed)
{
canRead = false;
isDisposed = true;
}
base.Dispose(disposing);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ public async Task OneDriveUploadLargeFile()

// Setup the chunk request necessities
var chunkRequests = provider.GetUploadChunkRequests();
var readBuffer = new byte[maxChunkSize];
var trackedExceptions = new List<Exception>();
DriveItem itemResult = null;

Expand All @@ -71,7 +70,7 @@ public async Task OneDriveUploadLargeFile()
// Do your updates here: update progress bar, etc.
// ...
// Send chunk request
var result = await provider.GetChunkRequestResponseAsync(request, readBuffer, trackedExceptions);
var result = await provider.GetChunkRequestResponseAsync(request, trackedExceptions);

if (result.UploadSucceeded)
{
Expand Down Expand Up @@ -223,7 +222,7 @@ public async Task OneDriveGetContent()
{
var driveItemContent = await graphClient.Me.Drive.Items[item.Id].Content.Request().GetAsync();
Assert.NotNull(driveItemContent);
Assert.IsType(typeof(MemoryStream), driveItemContent);
Assert.IsType<MemoryStream>(driveItemContent);
return;
}
}
Expand Down Expand Up @@ -347,7 +346,7 @@ public async Task OneDriveInvite()
.Request()
.Filter("startswith(name,'Timesheet')")
.GetAsync();
Assert.True(itemToShare[0].Name.StartsWith("Timesheet"));
Assert.StartsWith("Timesheet", itemToShare[0].Name);

var me = await graphClient.Me.Request().GetAsync();
var domain = me.Mail.Split('@')[1];
Expand Down