-
Notifications
You must be signed in to change notification settings - Fork 214
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
632 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,7 @@ | |
'task', | ||
'task_assignment', | ||
'user', | ||
'upload_session', | ||
'webhook', | ||
'watermark', | ||
'web_link', | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
# coding: utf-8 | ||
from __future__ import unicode_literals, absolute_import | ||
|
||
import base64 | ||
import hashlib | ||
import json | ||
|
||
from .base_object import BaseObject | ||
from ..pagination.limit_offset_based_dict_collection import LimitOffsetBasedDictCollection | ||
|
||
|
||
class UploadSession(BaseObject): | ||
_item_type = 'upload_session' | ||
_parent_item_type = 'file' | ||
|
||
def get_url(self, *args): | ||
""" | ||
Base class override. Endpoint is a little different - it's /files/upload_sessions. | ||
:rtype: | ||
`unicode` | ||
""" | ||
return self._session.get_url( | ||
'{0}s/{1}s'.format(self._parent_item_type, self._item_type), | ||
self._object_id, | ||
*args | ||
).replace(self.session.api_config.BASE_API_URL, self.session.api_config.UPLOAD_URL) | ||
|
||
def get_parts(self, limit=None, offset=None): | ||
""" | ||
Get a list of parts uploaded so far. | ||
:param limit: | ||
The maximum number of items to return per page. If not specified, then will use the server-side default. | ||
:type limit: | ||
`int` or None | ||
:param offset: | ||
The index at which to start returning items. | ||
:type offset: | ||
`int` or None | ||
:param fields: | ||
Fields to include on the returned items. | ||
:type fields: | ||
`Iterable` of `unicode` | ||
:returns: | ||
Returns a :class:`BoxObjectCollection` object containing the uploaded parts. | ||
:rtype: | ||
:class:`BoxObjectCollection` | ||
""" | ||
return LimitOffsetBasedDictCollection( | ||
session=self.session, | ||
url=self.get_url('parts'), | ||
limit=limit, | ||
offset=offset, | ||
fields=None, | ||
return_full_pages=False, | ||
) | ||
|
||
def upload_part_bytes(self, part_bytes, offset, total_size, part_content_sha1=None): | ||
""" | ||
Upload a part of a file. | ||
:param part_bytes: | ||
Part bytes | ||
:type part_bytes: | ||
`bytes` | ||
:param offset: | ||
Offset, in number of bytes, of the part compared to the beginning of the file. This number should be a | ||
multiple of the part size. | ||
:type offset: | ||
`int` | ||
:param total_size: | ||
The size of the file that this part belongs to. | ||
:type total_size: | ||
`int` | ||
:param part_content_sha1: | ||
SHA-1 hash of the part's content. If not specified, this will be calculated. | ||
:type part_content_sha1: | ||
`bytes` or None | ||
:returns: | ||
The uploaded part record. | ||
:rtype: | ||
`dict` | ||
""" | ||
|
||
if part_content_sha1 is None: | ||
sha1 = hashlib.sha1() | ||
sha1.update(part_bytes) | ||
part_content_sha1 = sha1.digest() | ||
|
||
range_end = min(offset + self.part_size - 1, total_size - 1) # pylint:disable=no-member | ||
headers = { | ||
'Content-Type': 'application/octet-stream', | ||
'Digest': 'SHA={0}'.format(base64.b64encode(part_content_sha1).decode('utf-8')), | ||
'Content-Range': 'bytes {0}-{1}/{2}'.format(offset, range_end, total_size), | ||
} | ||
response = self._session.put( | ||
self.get_url(), | ||
headers=headers, | ||
data=part_bytes, | ||
) | ||
return response.json()['part'] | ||
|
||
def commit(self, content_sha1, parts=None, file_attributes=None, etag=None): | ||
""" | ||
Commit a multiput upload. | ||
:param content_sha1: | ||
SHA-1 hash of the file contents that was uploaded. | ||
:type content_sha1: | ||
`bytes` | ||
:param parts: | ||
List of parts that were uploaded. | ||
:type parts: | ||
`Iterable` of `dict` or None | ||
:param file_attributes: | ||
A `dict` of attributes to set on the uploaded file. | ||
:type file_attributes: | ||
`dict` | ||
:param etag: | ||
If specified, instruct the Box API to delete the folder only if the current version's etag matches. | ||
:type etag: | ||
`unicode` or None | ||
:returns: | ||
The newly-uploaded file object. | ||
:rtype: | ||
:class:`File` | ||
""" | ||
body = {} | ||
if file_attributes is not None: | ||
body['attributes'] = file_attributes | ||
if parts is not None: | ||
body['parts'] = parts | ||
else: | ||
body['parts'] = [part for part in self.get_parts()] | ||
headers = { | ||
'Content-Type': 'application/json', | ||
'Digest': 'SHA={0}'.format(base64.b64encode(content_sha1).decode('utf-8')), | ||
} | ||
if etag is not None: | ||
headers['If-Match'] = etag | ||
response = self._session.post( | ||
self.get_url('commit'), | ||
headers=headers, | ||
data=json.dumps(body), | ||
).json() | ||
entry = response['entries'][0] | ||
return self.translator.translate( | ||
session=self._session, | ||
response_object=entry, | ||
) | ||
|
||
def abort(self): | ||
""" | ||
Abort an upload session, cancelling the upload and removing any parts that have already been uploaded. | ||
:returns: | ||
A boolean indication success of the upload abort. | ||
:rtype: | ||
`bool` | ||
""" | ||
return self.delete() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# coding: utf-8 | ||
|
||
from __future__ import unicode_literals, absolute_import | ||
|
||
from .page import Page | ||
|
||
|
||
class DictPage(Page): | ||
def __getitem__(self, key): | ||
item_json = self._response_object[self._item_entries_key_name][key] | ||
|
||
return item_json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# coding: utf-8 | ||
|
||
from __future__ import unicode_literals, absolute_import | ||
|
||
from .dict_page import DictPage | ||
from .limit_offset_based_object_collection import LimitOffsetBasedObjectCollection | ||
|
||
|
||
class LimitOffsetBasedDictCollection(LimitOffsetBasedObjectCollection): | ||
_page_constructor = DictPage |
Oops, something went wrong.