Skip to content

Commit 1508bff

Browse files
authored
Merge pull request #151 from box/support-content-md5-for-file-upload
Support content md5 for file upload
2 parents 4038723 + 1f5a039 commit 1508bff

File tree

10 files changed

+179
-28
lines changed

10 files changed

+179
-28
lines changed

Box.V2.Test/app.config

-15
This file was deleted.

Box.V2/Box.V2.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
<Compile Include="Models\Permissions\BoxItemPermission.cs" />
8383
<Compile Include="Models\BoxSortOrder.cs" />
8484
<Compile Include="Models\Request\BoxMetadataFilterRequest.cs" />
85+
<Compile Include="Models\Request\BoxPreflightCheckRequest.cs" />
8586
<Compile Include="Models\Request\BoxRetentionPolicyAssignmentRequest.cs" />
8687
<Compile Include="Models\Request\BoxRetentionPolicyRequest.cs" />
8788
<Compile Include="Models\Request\BoxUserInviteRequest.cs" />

Box.V2/Box.V2.nuspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<package >
33
<metadata>
44
<id>Box.V2</id>
5-
<version>2.9.0</version>
5+
<version>2.10.0</version>
66
<title>Box Windows SDK V2</title>
77
<authors>Box, Inc.</authors>
88
<owners>Box, Inc.</owners>

Box.V2/Config/BoxConfig.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Box.V2.Config
77
{
88
public class BoxConfig : IBoxConfig
99
{
10-
private const string DefaultUserAgent = "Box Windows SDK v2.7.0";
10+
private const string DefaultUserAgent = "Box Windows SDK v2.10.0";
1111

1212
/// <summary>
1313
/// Instantiates a Box config with all of the standard defaults
@@ -74,7 +74,9 @@ public BoxConfig(string clientId, string clientSecret, string enterpriseId,
7474
public virtual Uri FoldersEndpointUri { get { return new Uri(BoxApiUri, Constants.FoldersString); } }
7575
public virtual Uri FilesEndpointUri { get { return new Uri(BoxApiUri, Constants.FilesString); } }
7676
public virtual Uri FilesUploadEndpointUri { get { return new Uri(BoxUploadApiUri, Constants.FilesUploadString); } }
77-
public virtual Uri FilesNewVersionEndpointUri { get { return new Uri(BoxUploadApiUri, Constants.FilesNewVersionString); } }
77+
//public virtual Uri FilesNewVersionEndpointUri { get { return new Uri(BoxUploadApiUri, Constants.FilesNewVersionString); } }
78+
public virtual Uri FilesPreflightCheckUri { get { return new Uri(BoxApiUri, Constants.FilesUploadString); } }
79+
//public virtual Uri FilesPreflightCheckNewVersionUri { get { return new Uri(BoxApiUri, Constants.FilesNewVersionString); } }
7880
public virtual Uri CommentsEndpointUri { get { return new Uri(BoxApiUri, Constants.CommentsString); } }
7981
public virtual Uri SearchEndpointUri { get { return new Uri(BoxApiUri, Constants.SearchString); } }
8082
public virtual Uri UserEndpointUri { get { return new Uri(BoxApiUri, Constants.UserString); } }

Box.V2/Config/Constants.cs

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public static class Constants
4242
public const string FilesEndpointString = BoxApiUriString + FilesString;
4343
public const string FilesUploadEndpointString = BoxUploadApiUriString + FilesUploadString;
4444
public const string FilesNewVersionEndpointString = BoxUploadApiUriString + FilesNewVersionString;
45+
public const string FilesPreflightCheckNewVersionString = BoxApiUriString + FilesNewVersionString;
4546
public const string CommentsEndpointString = BoxApiUriString + CommentsString;
4647
public const string SearchEndpointString = BoxApiUriString + SearchString;
4748
public const string UserEndpointString = BoxApiUriString + UserString;
@@ -116,6 +117,8 @@ public static class RequestParameters
116117

117118
public const string AsUser = "As-User";
118119

120+
public const string ContentMD5 = "Content-MD5";
121+
119122
/*** Values ***/
120123
public const string RefreshToken = "refresh_token";
121124
public const string AuthorizationCode = "authorization_code";

Box.V2/Config/IBoxConfig.cs

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public interface IBoxConfig
3535
Uri FoldersEndpointUri { get; }
3636
Uri FilesEndpointUri { get; }
3737
Uri FilesUploadEndpointUri { get; }
38+
Uri FilesPreflightCheckUri { get; }
39+
//Uri FilesPreflightCheckNewVersionUri { get; }
3840
Uri CommentsEndpointUri { get; }
3941
Uri SearchEndpointUri { get; }
4042
Uri UserEndpointUri { get; }

Box.V2/Managers/BoxFilesManager.cs

+73-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Linq;
1111
using System.Net;
1212
using System.Net.Http.Headers;
13+
using System.Text;
1314
using System.Threading.Tasks;
1415

1516
namespace Box.V2.Managers
@@ -81,6 +82,47 @@ public async Task<Uri> GetDownloadUriAsync(string id, string versionId = null)
8182
return locationUri;
8283
}
8384

85+
/// <summary>
86+
/// Verify that a file will be accepted by Box before you send all the bytes over the wire.
87+
/// </summary>
88+
/// <param name="preflightCheckRequest"></param>
89+
/// <returns></returns>
90+
public async Task PreflightCheck(BoxPreflightCheckRequest preflightCheckRequest)
91+
{
92+
preflightCheckRequest.ThrowIfNull("preflightCheckRequest")
93+
.Name.ThrowIfNullOrWhiteSpace("preflightCheckRequest.Name");
94+
preflightCheckRequest.Parent.ThrowIfNull("preflightCheckRequest.Parent")
95+
.Id.ThrowIfNullOrWhiteSpace("preflightCheckRequest.Parent.Id");
96+
97+
BoxRequest request = new BoxRequest(_config.FilesPreflightCheckUri)
98+
.Method(RequestMethod.Options);
99+
100+
request.Payload = _converter.Serialize(preflightCheckRequest);
101+
request.ContentType = Constants.RequestParameters.ContentTypeJson;
102+
103+
IBoxResponse<BoxEntity> response = await ToResponseAsync<BoxEntity>(request).ConfigureAwait(false);
104+
}
105+
106+
/// <summary>
107+
/// Verify that a new version of a file will be accepted by Box before you send all the bytes over the wire.
108+
/// </summary>
109+
/// <param name="fileId"></param>
110+
/// <param name="preflightCheckRequest"></param>
111+
/// <returns></returns>
112+
public async Task PreflightCheckNewVersion(string fileId, BoxPreflightCheckRequest preflightCheckRequest)
113+
{
114+
if (preflightCheckRequest.Size <= 0)
115+
throw new ArgumentException("Size in bytes must be greater than zero (otherwise preflight check for new version would always succeed)", "sizeinBytes");
116+
117+
BoxRequest request = new BoxRequest(new Uri(string.Format(Constants.FilesPreflightCheckNewVersionString, fileId)))
118+
.Method(RequestMethod.Options);
119+
120+
request.Payload = _converter.Serialize(preflightCheckRequest);
121+
request.ContentType = Constants.RequestParameters.ContentTypeJson;
122+
123+
IBoxResponse<BoxEntity> response = await ToResponseAsync<BoxEntity>(request).ConfigureAwait(false);
124+
}
125+
84126
/// <summary>
85127
/// Uploads a provided file to the target parent folder
86128
/// If the file already exists, an error will be thrown.
@@ -90,15 +132,20 @@ public async Task<Uri> GetDownloadUriAsync(string id, string versionId = null)
90132
/// <param name="stream"></param>
91133
/// <param name="fields"></param>
92134
/// <param name="timeout"></param>
135+
/// <param name="contentMD5"></param>
136+
/// <param name="setStreamPositionToZero"></param>
93137
/// <returns></returns>
94-
public async Task<BoxFile> UploadAsync(BoxFileRequest fileRequest, Stream stream, List<string> fields = null, TimeSpan? timeout = null)
138+
public async Task<BoxFile> UploadAsync(BoxFileRequest fileRequest, Stream stream, List<string> fields = null, TimeSpan? timeout = null, byte[] contentMD5 = null, bool setStreamPositionToZero = true)
95139
{
96140
stream.ThrowIfNull("stream");
97141
fileRequest.ThrowIfNull("fileRequest")
98142
.Name.ThrowIfNullOrWhiteSpace("filedRequest.Name");
99143
fileRequest.Parent.ThrowIfNull("fileRequest.Parent")
100144
.Id.ThrowIfNullOrWhiteSpace("fileRequest.Parent.Id");
101145

146+
if (setStreamPositionToZero)
147+
stream.Position = 0;
148+
102149
BoxMultiPartRequest request = new BoxMultiPartRequest(_config.FilesUploadEndpointUri) { Timeout = timeout }
103150
.Param(ParamFields, fields)
104151
.FormPart(new BoxStringFormPart()
@@ -113,6 +160,9 @@ public async Task<BoxFile> UploadAsync(BoxFileRequest fileRequest, Stream stream
113160
FileName = fileRequest.Name
114161
});
115162

163+
if (contentMD5 != null)
164+
request.Header(Constants.RequestParameters.ContentMD5, HexStringFromBytes(contentMD5));
165+
116166
IBoxResponse<BoxCollection<BoxFile>> response = await ToResponseAsync<BoxCollection<BoxFile>>(request).ConfigureAwait(false);
117167

118168
// We can only upload one file at a time, so return the first entry
@@ -126,15 +176,22 @@ public async Task<BoxFile> UploadAsync(BoxFileRequest fileRequest, Stream stream
126176
/// A proper timeout should be provided for large uploads
127177
/// </summary>
128178
/// <param name="fileName"></param>
179+
/// <param name="fileId"
129180
/// <param name="stream"></param>
130181
/// <param name="etag"></param>
182+
/// <param name="fields"></param>
131183
/// <param name="timeout"></param>
184+
/// <param name="contentMD5"></param>
185+
/// <param name="setStreamPositionToZero"></param>
132186
/// <returns></returns>
133-
public async Task<BoxFile> UploadNewVersionAsync(string fileName, string fileId, Stream stream, string etag = null, List<string> fields = null, TimeSpan? timeout = null)
187+
public async Task<BoxFile> UploadNewVersionAsync(string fileName, string fileId, Stream stream, string etag = null, List<string> fields = null, TimeSpan? timeout = null, byte[] contentMD5 = null, bool setStreamPositionToZero = true)
134188
{
135189
stream.ThrowIfNull("stream");
136190
fileName.ThrowIfNullOrWhiteSpace("fileName");
137191

192+
if (setStreamPositionToZero)
193+
stream.Position = 0;
194+
138195
BoxMultiPartRequest request = new BoxMultiPartRequest(new Uri(string.Format(Constants.FilesNewVersionEndpointString, fileId))) { Timeout = timeout }
139196
.Header("If-Match", etag)
140197
.Param(ParamFields, fields)
@@ -145,12 +202,26 @@ public async Task<BoxFile> UploadNewVersionAsync(string fileName, string fileId,
145202
FileName = fileName
146203
});
147204

205+
if (contentMD5 != null)
206+
request.Header(Constants.RequestParameters.ContentMD5, HexStringFromBytes(contentMD5));
207+
148208
IBoxResponse<BoxCollection<BoxFile>> response = await ToResponseAsync<BoxCollection<BoxFile>>(request).ConfigureAwait(false);
149209

150210
// We can only upload one file at a time, so return the first entry
151211
return response.ResponseObject.Entries.FirstOrDefault();
152212
}
153213

214+
private string HexStringFromBytes(byte[] bytes)
215+
{
216+
var sb = new StringBuilder();
217+
foreach (byte b in bytes)
218+
{
219+
var hex = b.ToString("x2");
220+
sb.Append(hex);
221+
}
222+
return sb.ToString();
223+
}
224+
154225
/// <summary>
155226
/// If there are previous versions of this file, this method can be used to retrieve metadata about the older versions.
156227
/// <remarks>Versions are only tracked for Box users with premium accounts.</remarks>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Newtonsoft.Json;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
7+
namespace Box.V2.Models
8+
{
9+
/// <summary>
10+
/// A request class for making preflight check requests
11+
/// </summary>
12+
public class BoxPreflightCheckRequest : BoxItemRequest
13+
{
14+
/// <summary>
15+
/// The size of the file in bytes. Specify 0 for unknown file-sizes
16+
/// </summary>
17+
[JsonProperty(PropertyName = "size")]
18+
public long Size { get; set; }
19+
}
20+
}

Box.V2/Request/HttpRequestHandler.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using Box.V2.Config;
2+
using System;
23
using System.Diagnostics;
34
using System.IO;
45
using System.Linq;
@@ -24,7 +25,16 @@ public async Task<IBoxResponse<T>> ExecuteAsync<T>(IBoxRequest request)
2425

2526
// Add headers
2627
foreach (var kvp in request.HttpHeaders)
27-
httpRequest.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value);
28+
{
29+
if (kvp.Key == Constants.RequestParameters.ContentMD5)
30+
{
31+
httpRequest.Content.Headers.Add(kvp.Key, kvp.Value);
32+
} else
33+
{
34+
httpRequest.Headers.TryAddWithoutValidation(kvp.Key, kvp.Value);
35+
}
36+
}
37+
2838

2939
// If we are retrieving a stream, we should return without reading the entire response
3040
HttpCompletionOption completionOption = isStream ?

README.md

+63-6
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,73 @@ BoxFileRequest request = new BoxFileRequest()
120120
BoxFile f = await client.FilesManager.UpdateInformationAsync(request );
121121
```
122122

123-
124123
#### Upload a New File
125124
```c#
125+
BoxFile newFile;
126+
126127
// Create request object with name and parent folder the file should be uploaded to
127-
BoxFileRequest req = new BoxFileRequest()
128+
using (FileStream stream = new FileStream(@"C:\\example.pdf", FileMode.Open))
128129
{
129-
Name = "NewFile",
130-
Parent = new BoxRequestEntity() { Id = "0" }
131-
};
132-
BoxFile f = await client.FilesManager.UploadAsync(request, stream);
130+
BoxFileRequest req = new BoxFileRequest()
131+
{
132+
Name = "example.pdf",
133+
Parent = new BoxRequestEntity() { Id = "0" }
134+
};
135+
newFile = await client.FilesManager.UploadAsync(req, stream);
136+
}
137+
```
138+
139+
#### Upload a New File with Content MD5 hash
140+
```c#
141+
BoxFile newFile;
142+
143+
// Create request object with name and parent folder the file should be uploaded to
144+
using (FileStream stream = new FileStream(@"C:\\example.pdf", FileMode.Open))
145+
using (SHA1 sha1 = SHA1.Create())
146+
{
147+
BoxFileRequest req = new BoxFileRequest()
148+
{
149+
Name = "example.pdf",
150+
Parent = new BoxRequestEntity() { Id = "0" }
151+
};
152+
153+
byte[] md5Bytes = sha1.ComputeHash(fs);
154+
155+
newFile = await client.FilesManager.UploadAsync(req, stream, contentMD5: md5Bytes);
156+
}
157+
```
158+
159+
#### Perform Preflight Check for a new file upload
160+
```c#
161+
try
162+
{
163+
var req = new BoxPreflightCheckRequest() { Name = "example.pdf",
164+
Parent = new BoxRequestEntity() { Id = "0" },
165+
Size = 10000 //set the size if known, otherwise don't set (i.e. for a stream)
166+
};
167+
168+
//exception will be thrown if name collision or storage limit would be exceeded by upload
169+
await userClient.FilesManager.PreflightCheck(req);
170+
}
171+
catch (BoxException bex)
172+
{
173+
//Handle error
174+
}
175+
```
176+
177+
#### Perform Preflight Check for a new version of file
178+
```c#
179+
try
180+
{
181+
var req = new BoxPreflightCheckRequest() { Size=10926 };
182+
183+
//exception will be thrown if storage limit would be exceeded by uploading new version of file
184+
await userClient.FilesManager.PreflightCheckNewVersion(existingFile.Id, req);
185+
}
186+
catch (BoxException bex)
187+
{
188+
//Handle error
189+
}
133190
```
134191

135192
#### Download a File

0 commit comments

Comments
 (0)