Skip to content

Commit

Permalink
Merge pull request #284 from box/mupload-p1
Browse files Browse the repository at this point in the history
MultiPut upload part1
  • Loading branch information
coolboy committed Feb 24, 2017
2 parents 1052c9d + ac4d3b2 commit 0ac3e1b
Show file tree
Hide file tree
Showing 15 changed files with 379 additions and 37 deletions.
10 changes: 8 additions & 2 deletions Box.V2.Core/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"../Box.V2/Models/BoxFileEventSource.cs",
"../Box.V2/Models/BoxFileLock.cs",
"../Box.V2/Models/BoxFilePreview.cs",
"../Box.V2/Models/BoxFileUploadSession.cs",
"../Box.V2/Models/BoxFileVersionLegalHold.cs",
"../Box.V2/Models/BoxFileVersionRetention.cs",
"../Box.V2/Models/BoxLegalHoldPolicyAssignment.cs",
Expand All @@ -77,6 +78,7 @@
"../Box.V2/Models/BoxFileVersion.cs",
"../Box.V2/Models/BoxGroup.cs",
"../Box.V2/Models/BoxGroupMembership.cs",
"../Box.V2/Models/BoxSessionEndpoint.cs",
"../Box.V2/Models/Request/BoxActionableByRequest.cs",
"../Box.V2/Models/Request/BoxLegalHoldPolicyAssignmentRequest.cs",
"../Box.V2/Models/Request/BoxLegalHoldPolicyRequest.cs",
Expand All @@ -87,6 +89,7 @@
"../Box.V2/Models/Request/BoxWebLinkRequest.cs",
"../Box.V2/Models/Request/BoxEmailAliasRequest.cs",
"../Box.V2/Models/Request/BoxFileLockRequest.cs",
"../Box.V2/Models/Request/BoxFileUploadSessionRequest.cs",
"../Box.V2/Models/Request/BoxDeleteSharedLinkRequest.cs",
"../Box.V2/Models/Request/BoxFileVersionRetentionRequest.cs",
"../Box.V2/Models/Request/BoxGroupMembershipRequest.cs",
Expand All @@ -113,14 +116,16 @@
"../Box.V2/Utility/LRUCache.cs",
"../Box.V2/Utility/CrossPlatform.cs",
"../Box.V2/Utility/Helper.cs",
"../Box.V2/Extensions/BoxResponseExtensions.cs",
"../Box.V2/Wrappers/BoxError.cs",
"../Box.V2/Wrappers/BoxErrorContextInfo.cs",
"../Box.V2/Wrappers/BoxMultiPartRequest.cs",
"../Box.V2/Wrappers/BoxResponse.cs",
"../Box.V2/Extensions/BoxResponseExtensions.cs",
"../Box.V2/Wrappers/Contracts/IBoxResponse.cs",
"../Box.V2/Wrappers/BoxFormPart.cs",
"../Box.V2/Wrappers/BoxPart.cs",
"../Box.V2/Wrappers/Contracts/IBoxResponse.cs",
"../Box.V2/Wrappers/Contracts/IBoxFormPart.cs",
"../Box.V2/Wrappers/Contracts/IBoxPart.cs",
"../Box.V2/Config/BoxConfig.cs",
"../Box.V2/Config/Constants.cs",
"../Box.V2/Auth/IAuthRepository.cs",
Expand Down Expand Up @@ -159,6 +164,7 @@
"../Box.V2/Models/Request/BoxCollaborationRequest.cs",
"../Box.V2/Models/Request/BoxCollaborationUserRequest.cs",
"../Box.V2/Wrappers/BoxRequest.cs",
"../Box.V2/Wrappers/BoxBinaryRequest.cs",
"../Box.V2/Wrappers/Contracts/IBoxRequest.cs",
"../Box.V2/Extensions/BoxRequestExtensions.cs",
"../Box.V2/Services/BoxService.cs",
Expand Down
6 changes: 6 additions & 0 deletions Box.V2/Box.V2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@
<Compile Include="Models\BoxEmailAlias.cs" />
<Compile Include="Models\BoxEnterprise.cs" />
<Compile Include="Models\BoxEnterpriseEvent.cs" />
<Compile Include="Models\BoxSessionEndpoint.cs" />
<Compile Include="Models\BoxFileUploadSession.cs" />
<Compile Include="Models\BoxExpiringEmbedLink.cs" />
<Compile Include="Models\BoxFileEventSource.cs" />
<Compile Include="Models\BoxFileLock.cs" />
Expand Down Expand Up @@ -100,6 +102,7 @@
<Compile Include="Models\BoxGroup.cs" />
<Compile Include="Models\BoxGroupMembership.cs" />
<Compile Include="Models\Request\BoxActionableByRequest.cs" />
<Compile Include="Models\Request\BoxFileUploadSessionRequest.cs" />
<Compile Include="Models\Request\BoxLegalHoldPolicyAssignmentRequest.cs" />
<Compile Include="Models\Request\BoxLegalHoldPolicyRequest.cs" />
<Compile Include="Models\Request\BoxTaskAssignmentUpdateRequest.cs" />
Expand Down Expand Up @@ -137,9 +140,12 @@
<Compile Include="Utility\LRUCache.cs" />
<Compile Include="Wrappers\BoxError.cs" />
<Compile Include="Wrappers\BoxErrorContextInfo.cs" />
<Compile Include="Wrappers\BoxBinaryRequest.cs" />
<Compile Include="Wrappers\BoxPart.cs" />
<Compile Include="Wrappers\BoxMultiPartRequest.cs" />
<Compile Include="Wrappers\BoxResponse.cs" />
<Compile Include="Extensions\BoxResponseExtensions.cs" />
<Compile Include="Wrappers\Contracts\IBoxPart.cs" />
<Compile Include="Wrappers\Contracts\IBoxResponse.cs" />
<Compile Include="Wrappers\BoxFormPart.cs" />
<Compile Include="Wrappers\Contracts\IBoxFormPart.cs" />
Expand Down
9 changes: 7 additions & 2 deletions Box.V2/Config/BoxConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,10 @@ public BoxConfig(string clientId, string clientSecret, string enterpriseId,
UserAgent = DefaultUserAgent;
}



public virtual Uri BoxApiHostUri { get { return new Uri(Constants.BoxApiHostUriString); } }
public virtual Uri BoxApiUri { get { return new Uri(Constants.BoxApiUriString); } }
public virtual Uri BoxUploadApiUri { get { return new Uri(Constants.BoxUploadApiUriString); } }
public virtual Uri BoxUploadApiUriV21 { get { return new Uri(Constants.BoxUploadApiUriV21String); } }

public virtual string ClientId { get; private set; }
public virtual string ConsumerKey { get; private set; }
Expand All @@ -71,6 +70,12 @@ public BoxConfig(string clientId, string clientSecret, string enterpriseId,
public virtual Uri FoldersEndpointUri { get { return new Uri(BoxApiUri, Constants.FoldersString); } }
public virtual Uri FilesEndpointUri { get { return new Uri(BoxApiUri, Constants.FilesString); } }
public virtual Uri FilesUploadEndpointUri { get { return new Uri(BoxUploadApiUri, Constants.FilesUploadString); } }

/// <summary>
/// Upload session
/// </summary>
public virtual Uri FilesUploadSessionEndpointUri { get { return new Uri(BoxUploadApiUriV21, Constants.FilesUploadSessionString); } }

//public virtual Uri FilesNewVersionEndpointUri { get { return new Uri(BoxUploadApiUri, Constants.FilesNewVersionString); } }
public virtual Uri FilesPreflightCheckUri { get { return new Uri(BoxApiUri, Constants.FilesUploadString); } }
//public virtual Uri FilesPreflightCheckNewVersionUri { get { return new Uri(BoxApiUri, Constants.FilesNewVersionString); } }
Expand Down
9 changes: 9 additions & 0 deletions Box.V2/Config/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public static class Constants
public const string BoxApiHostUriString = "https://app.box.com/api/";
public const string BoxApiUriString = "https://api.box.com/2.0/";
public const string BoxUploadApiUriString = "https://upload.box.com/api/2.0/";
public const string BoxUploadApiUriV21String = "https://upload.box.com/api/2.1/";


/*** API Endpoints ***/
Expand All @@ -25,7 +26,9 @@ public static class Constants
public const string GroupMembershipString = @"group_memberships/";
public const string FilesString = @"files/";
public const string FilesUploadString = @"files/content";
public const string FilesUploadSessionString = @"files/upload-session";
public const string FilesNewVersionString = @"files/{0}/content";
public const string FilesNewVersionUploadSessionString = @"files/{0}/upload-session";
public const string CommentsString = @"comments/";
public const string SearchString = @"search";
public const string UserString = @"users/";
Expand Down Expand Up @@ -76,6 +79,7 @@ public static class Constants
public const string FilesEndpointString = BoxApiUriString + FilesString;
public const string FilesUploadEndpointString = BoxUploadApiUriString + FilesUploadString;
public const string FilesNewVersionEndpointString = BoxUploadApiUriString + FilesNewVersionString;
public const string FilesNewVersionUploadSessionEndpointString = BoxUploadApiUriV21String + FilesNewVersionUploadSessionString;
public const string FilesPreflightCheckNewVersionString = BoxApiUriString + FilesNewVersionString;
public const string CommentsEndpointString = BoxApiUriString + CommentsString;
public const string SearchEndpointString = BoxApiUriString + SearchString;
Expand Down Expand Up @@ -178,8 +182,13 @@ public static class RequestParameters
public const string AsUser = "As-User";

public const string BoxNotifications = "Box-Notifications";
public const string BoxPartId = "X-Box-Part-Id";

public const string ContentMD5 = "Content-MD5";
public const string ContentRange = "Content-Range";
public const string ContentLength = "Content-Length";

public const string Digest = "Digest";

/*** Values ***/
public const string RefreshToken = "refresh_token";
Expand Down
6 changes: 6 additions & 0 deletions Box.V2/Config/IBoxConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public interface IBoxConfig
Uri FoldersEndpointUri { get; }
Uri FilesEndpointUri { get; }
Uri FilesUploadEndpointUri { get; }

/// <summary>
/// Upload session
/// </summary>
Uri FilesUploadSessionEndpointUri { get; }

Uri FilesPreflightCheckUri { get; }
//Uri FilesPreflightCheckNewVersionUri { get; }
Uri CommentsEndpointUri { get; }
Expand Down
6 changes: 6 additions & 0 deletions Box.V2/Extensions/BoxRequestExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,11 @@ public static T FormPart<T>(this T request, IBoxFormPart formPart) where T : Box
return request;
}

public static T Part<T>(this T request, IBoxPart part) where T : BoxBinaryRequest
{
request.Part = part;

return request;
}
}
}
86 changes: 86 additions & 0 deletions Box.V2/Managers/BoxFilesManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,26 @@ public async Task<BoxFile> UploadAsync(BoxFileRequest fileRequest, Stream stream
return response.ResponseObject.Entries.FirstOrDefault();
}

/// <summary>
/// Create an upload session for uploading a new file.
/// </summary>
/// <param name="uploadSessionRequest">The upload session request.</param>
/// <returns>The upload session.</returns>
public async Task<BoxFileUploadSession> CreateUploadSessionAsync(BoxFileUploadSessionRequest uploadSessionRequest)
{
var uploadUri = _config.FilesUploadSessionEndpointUri;

var request = new BoxRequest(uploadUri)
.Method(RequestMethod.Post)
.Payload("folder_id", uploadSessionRequest.FolderId)
.Payload("file_size", uploadSessionRequest.FileSize)
.Payload("file_name", uploadSessionRequest.FileName);

IBoxResponse<BoxFileUploadSession> response = await ToResponseAsync<BoxFileUploadSession>(request).ConfigureAwait(false);

return response.ResponseObject;
}

/// <summary>
/// This method is used to upload a new version of an existing file in a user’s account. Similar to regular file uploads,
/// these are performed as multipart form uploads. An optional If-Match header can be included to ensure that client only
Expand Down Expand Up @@ -238,6 +258,55 @@ public async Task<BoxFile> UploadNewVersionAsync(string fileName, string fileId,
return response.ResponseObject.Entries.FirstOrDefault();
}

/// <summary>
/// Create new file version upload session.
/// </summary>
/// <param name="fileId">The file id.</param>
/// <param name="fileSize">The total number of bytes in the file to be uploaded.</param>
/// <returns></returns>
public async Task<BoxFileUploadSession> CreateNewVersionUploadSessionAsync(string fileId, string fileSize)
{
var uploadUri = new Uri(string.Format(Constants.FilesNewVersionUploadSessionEndpointString, fileId));

var request = new BoxRequest(uploadUri)
.Method(RequestMethod.Post)
.Payload("file_size", fileSize);

IBoxResponse<BoxFileUploadSession> response = await ToResponseAsync<BoxFileUploadSession>(request).ConfigureAwait(false);

return response.ResponseObject;
}

/// <summary>
/// Upload a part of the file to the session.
/// </summary>
/// <param name="id">The session id.</param>
/// <param name="sha1">The message digest of the part body, formatted as specified by RFC 3230.</param>
/// <param name="partId">A valid 8 character hex string that identifies the part upload request.</param>
/// <param name="partStartOffsetInBytes">Part begin offset in bytes.</param>
/// <param name="sizeOfOriginalFileInBytes">Size of original file in bytes.</param>
/// <param name="stream">The file part stream.</param>
/// <returns></returns>
public async Task<bool> UploadPartAsync(string id, string sha1, string partId, long partStartOffsetInBytes, long sizeOfOriginalFileInBytes, Stream stream)
{
var uploadUri = new Uri(_config.FilesUploadSessionEndpointUri.ToString() + "/" + id);

var binary = stream.ToString();

var request = new BoxBinaryRequest(uploadUri)
.Method(RequestMethod.Put)
.Header(Constants.RequestParameters.Digest, "sha=" + sha1)
.Header(Constants.RequestParameters.BoxPartId, partId)
.Header(Constants.RequestParameters.ContentRange, "bytes " + partStartOffsetInBytes.ToString() + "-" + (stream.Length - 1) + "/" + sizeOfOriginalFileInBytes)
.Part(new BoxFilePart() {
Value = stream
});

var response = await ToResponseAsync<object>(request).ConfigureAwait(false);

return response.Status == ResponseStatus.Success;
}

private string HexStringFromBytes(byte[] bytes)
{
var sb = new StringBuilder();
Expand Down Expand Up @@ -315,6 +384,23 @@ public async Task<bool> DeleteAsync(string id, string etag = null)
return response.Status == ResponseStatus.Success;
}

/// <summary>
/// Abort the upload session and discard all data uploaded. This cannot be reversed.
/// </summary>
/// <param name="id">The upload session id which this part belongs to.</param>
/// <returns>True if deletion success.</returns>
public async Task<bool> DeleteUploadSessionAsync(string id)
{
var uploadUri = new Uri(_config.FilesUploadSessionEndpointUri.ToString() + "/" + id);

var request = new BoxRequest(uploadUri)
.Method(RequestMethod.Delete);

IBoxResponse<BoxFileUploadSession> response = await ToResponseAsync<BoxFileUploadSession>(request).ConfigureAwait(false);

return response.Status == ResponseStatus.Success;
}

/// <summary>
/// Used to create a copy of a file in another folder. The original version of the file will not be altered.
/// </summary>
Expand Down
39 changes: 39 additions & 0 deletions Box.V2/Models/BoxFileUploadSession.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Newtonsoft.Json;

namespace Box.V2.Models
{
/// <summary>
/// Represents box upload session
/// </summary>
public class BoxFileUploadSession
{
public const string FieldUploadSessionId = "upload_session_id";
public const string FieldSessionExpiresAt = "session_expires_at";
public const string FieldPartSize = "part_size";
public const string FieldSessionEndpoints = "session_endpoints";

/// <summary>
/// The upload session id.
/// </summary>
[JsonProperty(PropertyName = FieldUploadSessionId)]
public string UploadSessionId { get; private set; }

/// <summary>
/// Session expiration time in RFC 3339.
/// </summary>
[JsonProperty(PropertyName = FieldSessionExpiresAt)]
public string SessionExpiresAt { get; private set; }

/// <summary>
/// The part sizein bytesthat must be used for all parts of this session. Only the last part is allowed to be of a smaller size.
/// </summary>
[JsonProperty(PropertyName = FieldPartSize)]
public string PartSize { get; private set; }

/// <summary>
/// URLs for all other possible calls to this session.
/// </summary>
[JsonProperty(PropertyName = FieldSessionEndpoints)]
public BoxSessionEndpoint SessionEndpoints { get; private set; }
}
}
46 changes: 46 additions & 0 deletions Box.V2/Models/BoxSessionEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Newtonsoft.Json;

namespace Box.V2.Models
{
/// <summary>
/// Represents box upload session
/// </summary>
public class BoxSessionEndpoint
{
public const string FieldListParts = "list_parts";
public const string FieldCommit = "commit";
public const string FieldLogEvent = "log_event";
public const string FieldUploadPart = "upload_part";
public const string FieldAbort = "abort";

/// <summary>
/// Endpoint to list parts.
/// </summary>
[JsonProperty(PropertyName = FieldListParts)]
public string ListParts { get; private set; }

/// <summary>
/// Endpoint to commit.
/// </summary>
[JsonProperty(PropertyName = FieldCommit)]
public string Commit { get; private set; }

/// <summary>
/// Endpoint to log event.
/// </summary>
[JsonProperty(PropertyName = FieldLogEvent)]
public string LogEvent { get; private set; }

/// <summary>
/// Endpoint to upload part.
/// </summary>
[JsonProperty(PropertyName = FieldUploadPart)]
public string UploadPart { get; private set; }

/// <summary>
/// Endpoint to abort.
/// </summary>
[JsonProperty(PropertyName = FieldAbort)]
public string Abort { get; private set; }
}
}
28 changes: 28 additions & 0 deletions Box.V2/Models/Request/BoxFileUploadSessionRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Newtonsoft.Json;

namespace Box.V2.Models
{
/// <summary>
/// A request class for creating a new file upload session
/// </summary>
public class BoxFileUploadSessionRequest
{
/// <summary>
/// The parent folder_id is the folder where the upload should happen.
/// </summary>
[JsonProperty(PropertyName = "folder_id")]
public string FolderId { get; set; }

/// <summary>
/// The total number of bytes in the file to be uploaded.
/// </summary>
[JsonProperty(PropertyName = "file_size")]
public string FileSize { get; set; }

/// <summary>
/// Name of new file.
/// </summary>
[JsonProperty(PropertyName = "file_name")]
public string FileName { get; set; }
}
}
Loading

0 comments on commit 0ac3e1b

Please sign in to comment.