Skip to content

Commit 58e9347

Browse files
committed
Add support for multipart file uploads
Files bigger than 200MB are uploaded in 50MB chunks via the multipart upload API. The upload progress can now be monitored by providing an implementor for `IProgress<UploadProgress>` with the `UploadOptions` struct.
1 parent 6355fdb commit 58e9347

File tree

14 files changed

+929
-36
lines changed

14 files changed

+929
-36
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace Modio;
2+
3+
public class ByteRangesTest
4+
{
5+
[Fact]
6+
public void TestCreateByteRanges()
7+
{
8+
const long CHUNK_SIZE = 50 * 1024 * 1024; // 50MB
9+
const long SIZE = 522 * 1024 * 1024;
10+
11+
(int, (long, long))[] expected = [
12+
(0, (0, 52428799)),
13+
(1, (52428800, 104857599)),
14+
(2, (104857600, 157286399)),
15+
(3, (157286400, 209715199)),
16+
(4, (209715200, 262143999)),
17+
(5, (262144000, 314572799)),
18+
(6, (314572800, 367001599)),
19+
(7, (367001600, 419430399)),
20+
(8, (419430400, 471859199)),
21+
(9, (471859200, 524287999)),
22+
(10, (524288000, 547356671)),
23+
];
24+
25+
var ranges = new ByteRanges(SIZE, CHUNK_SIZE);
26+
27+
Assert.Equal(11, ranges.Length);
28+
Assert.Equal(expected, ranges);
29+
}
30+
}

Modio.Tests/Helpers/StreamsTest.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
namespace Modio;
2+
3+
public class StreamsTest
4+
{
5+
class Progress : IProgress<long>
6+
{
7+
public long Total = 0;
8+
public void Report(long value)
9+
{
10+
Total += value;
11+
}
12+
}
13+
14+
public static TheoryData<long, long?, long, string> CopyBoundsData
15+
{
16+
get
17+
{
18+
return new TheoryData<long, long?, long, string>
19+
{
20+
{0, 26, 26, "abcdefghijklmnopqrstuvwxyz"},
21+
{0, null, 26, "abcdefghijklmnopqrstuvwxyz"},
22+
{5, null, 21, "fghijklmnopqrstuvwxyz"},
23+
{12, 10, 10, "mnopqrstuv"},
24+
};
25+
}
26+
}
27+
28+
[Theory]
29+
[MemberData(nameof(CopyBoundsData))]
30+
public async Task TestCopyToAsync(long start, long? count, long total, string expected)
31+
{
32+
var token = TestContext.Current.CancellationToken;
33+
var originalText = "abcdefghijklmnopqrstuvwxyz";
34+
35+
using (var innerStream = new MemoryStream())
36+
using (var writer = new StreamWriter(innerStream))
37+
using (var targetStream = new MemoryStream())
38+
using (var reader = new StreamReader(targetStream))
39+
{
40+
await writer.WriteAsync(originalText);
41+
await writer.FlushAsync(token);
42+
43+
var progress = new Progress();
44+
45+
innerStream.Position = start;
46+
await Streams.CopyToAsync(innerStream, targetStream, count, bufferSize: 5, token, progress);
47+
48+
targetStream.Position = 0;
49+
var text = await reader.ReadToEndAsync(token);
50+
Assert.Equal(expected, text);
51+
Assert.Equal(total, progress.Total);
52+
}
53+
}
54+
}

Modio/Clients/FilesClient.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,17 @@ public SearchClient<File> Search(Filter? filter = null)
4646
/// </summary>
4747
public async Task<File> Add(NewFile newFile, CancellationToken cancellationToken = default)
4848
{
49-
using (var content = newFile.ToContent())
50-
{
51-
var (method, path) = Routes.AddFile(GameId, ModId);
52-
var req = new Request(method, path, content);
49+
return await Add(newFile, default, cancellationToken);
50+
}
51+
52+
/// <summary>
53+
/// Upload a file for the corresponding mod.
54+
/// </summary>
55+
public async Task<File> Add(NewFile newFile, UploadOptions options = default, CancellationToken cancellationToken = default)
56+
{
57+
UploadClient client = new(Connection, GameId, ModId);
5358

54-
var resp = await Connection.Send<File>(req, cancellationToken);
55-
return resp.Body!;
56-
}
59+
return await client.UploadFile(newFile, options, cancellationToken);
5760
}
5861

5962
/// <summary>

0 commit comments

Comments
 (0)