Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Added the ability to upload files larger than 4MB #2180

Merged
merged 6 commits into from
Mar 23, 2018
Merged
9 changes: 7 additions & 2 deletions samples/AzCopyCore/AzCopyCore/Helpers/PipelinesExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,21 @@ public static async Task WriteAsync(this Stream stream, PipeReader reader, ulong
/// <summary>
/// Copies bytes from Stream to PipeWriter
/// </summary>
public static async Task WriteAsync(this PipeWriter writer, Stream stream)
public static async Task WriteAsync(this PipeWriter writer, Stream stream, long bytesToWrite)
{
if (!stream.CanRead) throw new ArgumentException("Stream.CanRead returned false", nameof(stream));
while (true)
while (bytesToWrite > 0)
{
Memory<byte> buffer = writer.GetMemory();
if(buffer.Length > bytesToWrite)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: formatting, add space

{
buffer = buffer.Slice(0, (int)bytesToWrite);
}
if (buffer.Length == 0) throw new NotSupportedException("PipeWriter.GetMemory returned an empty buffer.");
int read = await stream.ReadAsync(buffer).ConfigureAwait(false);
if (read == 0) return;
writer.Advance(read);
bytesToWrite -= read;
await writer.FlushAsync();
}
}
Expand Down
25 changes: 19 additions & 6 deletions samples/AzCopyCore/AzCopyCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static void PrintUsage()
static void Main(string[] args)
{
Log.Listeners.Add(new ConsoleTraceListener());
Log.Switch.Level = SourceLevels.Information;
Log.Switch.Level = SourceLevels.Error;

var options = new CommandLine(args);
ReadOnlySpan<char> source = options.GetSpan("/Source:");
Expand Down Expand Up @@ -197,14 +197,27 @@ static async ValueTask<bool> CopyLocalFileToStorageFile(StorageClient client, st
{
using (var bytes = new FileStream(localFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
{
var putRequest = new PutRangeRequest(storagePath, bytes);
response = await client.SendRequest(putRequest).ConfigureAwait(false);
if (response.StatusCode == 201) return true;
long bytesLeft = bytes.Length;
long index = 0;
int length = 1024 * 1024 * 4;
while (true)
{
if (bytesLeft < length) length = (int)bytesLeft;
var putRequest = new PutRangeRequest(storagePath, bytes, index, length);
response = await client.SendRequest(putRequest).ConfigureAwait(false);
if (response.StatusCode != 201)
{
Log.TraceEvent(TraceEventType.Error, 0, "Response Status Code {0}", response.StatusCode);
return false;
}
index += length;
bytesLeft -= length;
if (bytesLeft == 0) break;
Copy link
Member

@ahsonkhan ahsonkhan Mar 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why not use this as the while loop condition, especially since we do this check at the end of the loop anyway?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

}
}
}

Log.TraceEvent(TraceEventType.Error, 0, "Response Status Code {0}", response.StatusCode);
return false;
return true;
}

static async ValueTask<bool> CopyStorageFileToLocalFile(StorageClient client, string storagePath, string localFilePath)
Expand Down
36 changes: 25 additions & 11 deletions samples/AzCopyCore/AzCopyCore/StorageRequests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,30 @@ public static ReadOnlyMemory<byte> AsString(Http.Method verb)

public struct PutRangeRequest : IStorageRequest
{
public Stream FileContent { get; set; } // TODO (pri 3): should there be a way to write from file handle or PipeReader?
public string FilePath { get; set; }
Stream _fileContent; // TODO (pri 3): should there be a way to write from file handle or PipeReader?
string _filePath;
long _offset;
int _length;

// TODO (pri 3): I dont like how the client property is a public API
public StorageClient Client { get; set; }

public PutRangeRequest(string filePath, Stream fileContent)
public PutRangeRequest(string filePath, Stream fileContent, long offset, int length)
{
FilePath = filePath;
FileContent = fileContent;
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
Copy link
Member

@ahsonkhan ahsonkhan Mar 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to add checks for offset greater than _length?

Copy link
Member Author

@KrzysztofCwalina KrzysztofCwalina Mar 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

offset can be greater, equal, or less than length. It cannot be greater than fileContent.Length - _length. I will add the check.

if (length < 1) throw new ArgumentOutOfRangeException(nameof(length));

_filePath = filePath;
_fileContent = fileContent;
Client = null;
_offset = offset;
_length = length;
}

public long ContentLength => FileContent.Length;
public long ContentLength => _length;
// TODO (pri 2): would be nice to elimnate these allocations
public string RequestPath => FilePath + "?comp=range";
public string CanonicalizedResource => FilePath + "\ncomp:range";
public string RequestPath => _filePath + "?comp=range";
public string CanonicalizedResource => _filePath + "\ncomp:range";
public bool ConsumeBody => true;

// TODO (pri 3): can this be an extension method? All implementations are the same.
Expand All @@ -131,14 +138,21 @@ class Writer : StorageRequestWriter<PutRangeRequest>
public override Http.Method Verb => Http.Method.Put;

protected override async Task WriteBody(PipeWriter writer, PutRangeRequest arguments)
=> await writer.WriteAsync(arguments.FileContent);
{
var stream = arguments._fileContent;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use of var

stream.Seek(arguments._offset, SeekOrigin.Begin);
await writer.WriteAsync(stream, arguments._length);
}

protected override void WriteXmsHeaders(ref BufferWriter writer, ref PutRangeRequest arguments)
{
long size = arguments.FileContent.Length;
long size = arguments._fileContent.Length;
writer.WriteHeader("x-ms-date", Time, 'R');
// TODO (pri 3): this allocation should be eliminated
writer.WriteHeader("x-ms-range", $"bytes=0-{size - 1}");

var start = arguments._offset;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use of var

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will fix

var end = start + arguments._length - 1;
writer.WriteHeader("x-ms-range", $"bytes={start}-{end}");
writer.WriteHeader("x-ms-version", "2017-04-17");
writer.WriteHeader("x-ms-write", "update");
}
Expand Down