Move S3 storage to SpooledTemporaryFile
This commit is contained in:
parent
ed861ecae1
commit
b0a280bde2
@ -8,6 +8,7 @@ import itertools
|
|||||||
import codecs
|
import codecs
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
import tempfile
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ from .utils import clean_key_name, _VersionedKeyStore
|
|||||||
UPLOAD_ID_BYTES = 43
|
UPLOAD_ID_BYTES = 43
|
||||||
UPLOAD_PART_MIN_SIZE = 5242880
|
UPLOAD_PART_MIN_SIZE = 5242880
|
||||||
STORAGE_CLASS = ["STANDARD", "REDUCED_REDUNDANCY", "STANDARD_IA", "ONEZONE_IA"]
|
STORAGE_CLASS = ["STANDARD", "REDUCED_REDUNDANCY", "STANDARD_IA", "ONEZONE_IA"]
|
||||||
|
DEFAULT_KEY_BUFFER_SIZE = 2 ** 24
|
||||||
|
|
||||||
|
|
||||||
class FakeDeleteMarker(BaseModel):
|
class FakeDeleteMarker(BaseModel):
|
||||||
@ -42,9 +44,9 @@ class FakeDeleteMarker(BaseModel):
|
|||||||
|
|
||||||
class FakeKey(BaseModel):
|
class FakeKey(BaseModel):
|
||||||
|
|
||||||
def __init__(self, name, value, storage="STANDARD", etag=None, is_versioned=False, version_id=0):
|
def __init__(self, name, value, storage="STANDARD", etag=None, is_versioned=False, version_id=0,
|
||||||
|
max_buffer_size=DEFAULT_KEY_BUFFER_SIZE):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.value = value
|
|
||||||
self.last_modified = datetime.datetime.utcnow()
|
self.last_modified = datetime.datetime.utcnow()
|
||||||
self.acl = get_canned_acl('private')
|
self.acl = get_canned_acl('private')
|
||||||
self.website_redirect_location = None
|
self.website_redirect_location = None
|
||||||
@ -56,10 +58,24 @@ class FakeKey(BaseModel):
|
|||||||
self._is_versioned = is_versioned
|
self._is_versioned = is_versioned
|
||||||
self._tagging = FakeTagging()
|
self._tagging = FakeTagging()
|
||||||
|
|
||||||
|
self.value_buffer = tempfile.SpooledTemporaryFile(max_size=max_buffer_size)
|
||||||
|
self.value = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version_id(self):
|
def version_id(self):
|
||||||
return self._version_id
|
return self._version_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
self.value_buffer.seek(0)
|
||||||
|
return self.value_buffer.read()
|
||||||
|
|
||||||
|
@value.setter
|
||||||
|
def value(self, new_value):
|
||||||
|
self.value_buffer.seek(0)
|
||||||
|
self.value_buffer.truncate()
|
||||||
|
self.value_buffer.write(new_value)
|
||||||
|
|
||||||
def copy(self, new_name=None):
|
def copy(self, new_name=None):
|
||||||
r = copy.deepcopy(self)
|
r = copy.deepcopy(self)
|
||||||
if new_name is not None:
|
if new_name is not None:
|
||||||
@ -83,7 +99,9 @@ class FakeKey(BaseModel):
|
|||||||
self.acl = acl
|
self.acl = acl
|
||||||
|
|
||||||
def append_to_value(self, value):
|
def append_to_value(self, value):
|
||||||
self.value += value
|
self.value_buffer.seek(0, os.SEEK_END)
|
||||||
|
self.value_buffer.write(value)
|
||||||
|
|
||||||
self.last_modified = datetime.datetime.utcnow()
|
self.last_modified = datetime.datetime.utcnow()
|
||||||
self._etag = None # must recalculate etag
|
self._etag = None # must recalculate etag
|
||||||
if self._is_versioned:
|
if self._is_versioned:
|
||||||
@ -101,11 +119,14 @@ class FakeKey(BaseModel):
|
|||||||
def etag(self):
|
def etag(self):
|
||||||
if self._etag is None:
|
if self._etag is None:
|
||||||
value_md5 = hashlib.md5()
|
value_md5 = hashlib.md5()
|
||||||
if isinstance(self.value, six.text_type):
|
|
||||||
value = self.value.encode("utf-8")
|
self.value_buffer.seek(0)
|
||||||
else:
|
while True:
|
||||||
value = self.value
|
block = self.value_buffer.read(DEFAULT_KEY_BUFFER_SIZE)
|
||||||
value_md5.update(value)
|
if not block:
|
||||||
|
break
|
||||||
|
value_md5.update(block)
|
||||||
|
|
||||||
self._etag = value_md5.hexdigest()
|
self._etag = value_md5.hexdigest()
|
||||||
return '"{0}"'.format(self._etag)
|
return '"{0}"'.format(self._etag)
|
||||||
|
|
||||||
@ -132,7 +153,7 @@ class FakeKey(BaseModel):
|
|||||||
res = {
|
res = {
|
||||||
'ETag': self.etag,
|
'ETag': self.etag,
|
||||||
'last-modified': self.last_modified_RFC1123,
|
'last-modified': self.last_modified_RFC1123,
|
||||||
'content-length': str(len(self.value)),
|
'content-length': str(self.size),
|
||||||
}
|
}
|
||||||
if self._storage_class != 'STANDARD':
|
if self._storage_class != 'STANDARD':
|
||||||
res['x-amz-storage-class'] = self._storage_class
|
res['x-amz-storage-class'] = self._storage_class
|
||||||
@ -150,7 +171,8 @@ class FakeKey(BaseModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def size(self):
|
def size(self):
|
||||||
return len(self.value)
|
self.value_buffer.seek(0, os.SEEK_END)
|
||||||
|
return self.value_buffer.tell()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def storage_class(self):
|
def storage_class(self):
|
||||||
|
@ -524,7 +524,7 @@ def test_post_to_bucket():
|
|||||||
|
|
||||||
requests.post("https://foobar.s3.amazonaws.com/", {
|
requests.post("https://foobar.s3.amazonaws.com/", {
|
||||||
'key': 'the-key',
|
'key': 'the-key',
|
||||||
'file': 'nothing'
|
'file': b'nothing'
|
||||||
})
|
})
|
||||||
|
|
||||||
bucket.get_key('the-key').get_contents_as_string().should.equal(b'nothing')
|
bucket.get_key('the-key').get_contents_as_string().should.equal(b'nothing')
|
||||||
@ -538,7 +538,7 @@ def test_post_with_metadata_to_bucket():
|
|||||||
|
|
||||||
requests.post("https://foobar.s3.amazonaws.com/", {
|
requests.post("https://foobar.s3.amazonaws.com/", {
|
||||||
'key': 'the-key',
|
'key': 'the-key',
|
||||||
'file': 'nothing',
|
'file': b'nothing',
|
||||||
'x-amz-meta-test': 'metadata'
|
'x-amz-meta-test': 'metadata'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ def test_s3_server_post_to_bucket():
|
|||||||
|
|
||||||
test_client.post('/', "https://tester.localhost:5000/", data={
|
test_client.post('/', "https://tester.localhost:5000/", data={
|
||||||
'key': 'the-key',
|
'key': 'the-key',
|
||||||
'file': 'nothing'
|
'file': b'nothing'
|
||||||
})
|
})
|
||||||
|
|
||||||
res = test_client.get('/the-key', 'http://tester.localhost:5000/')
|
res = test_client.get('/the-key', 'http://tester.localhost:5000/')
|
||||||
|
@ -73,7 +73,7 @@ def test_s3_server_post_to_bucket():
|
|||||||
|
|
||||||
test_client.post('/foobar2', "https://localhost:5000/", data={
|
test_client.post('/foobar2', "https://localhost:5000/", data={
|
||||||
'key': 'the-key',
|
'key': 'the-key',
|
||||||
'file': 'nothing'
|
'file': b'nothing'
|
||||||
})
|
})
|
||||||
|
|
||||||
res = test_client.get('/foobar2/the-key', 'http://localhost:5000/')
|
res = test_client.get('/foobar2/the-key', 'http://localhost:5000/')
|
||||||
@ -89,7 +89,7 @@ def test_s3_server_put_ipv6():
|
|||||||
|
|
||||||
test_client.post('/foobar2', "https://[::]:5000/", data={
|
test_client.post('/foobar2', "https://[::]:5000/", data={
|
||||||
'key': 'the-key',
|
'key': 'the-key',
|
||||||
'file': 'nothing'
|
'file': b'nothing'
|
||||||
})
|
})
|
||||||
|
|
||||||
res = test_client.get('/foobar2/the-key', 'http://[::]:5000/')
|
res = test_client.get('/foobar2/the-key', 'http://[::]:5000/')
|
||||||
@ -105,7 +105,7 @@ def test_s3_server_put_ipv4():
|
|||||||
|
|
||||||
test_client.post('/foobar2', "https://127.0.0.1:5000/", data={
|
test_client.post('/foobar2', "https://127.0.0.1:5000/", data={
|
||||||
'key': 'the-key',
|
'key': 'the-key',
|
||||||
'file': 'nothing'
|
'file': b'nothing'
|
||||||
})
|
})
|
||||||
|
|
||||||
res = test_client.get('/foobar2/the-key', 'http://127.0.0.1:5000/')
|
res = test_client.get('/foobar2/the-key', 'http://127.0.0.1:5000/')
|
||||||
|
@ -198,7 +198,7 @@ def test_post_to_bucket():
|
|||||||
|
|
||||||
requests.post("https://s3.amazonaws.com/foobar", {
|
requests.post("https://s3.amazonaws.com/foobar", {
|
||||||
'key': 'the-key',
|
'key': 'the-key',
|
||||||
'file': 'nothing'
|
'file': b'nothing'
|
||||||
})
|
})
|
||||||
|
|
||||||
bucket.get_key('the-key').get_contents_as_string().should.equal(b'nothing')
|
bucket.get_key('the-key').get_contents_as_string().should.equal(b'nothing')
|
||||||
@ -212,7 +212,7 @@ def test_post_with_metadata_to_bucket():
|
|||||||
|
|
||||||
requests.post("https://s3.amazonaws.com/foobar", {
|
requests.post("https://s3.amazonaws.com/foobar", {
|
||||||
'key': 'the-key',
|
'key': 'the-key',
|
||||||
'file': 'nothing',
|
'file': b'nothing',
|
||||||
'x-amz-meta-test': 'metadata'
|
'x-amz-meta-test': 'metadata'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user