Ответ 1
Вы можете создать обработчик загрузки для загрузки файла непосредственно на s3. Таким образом, вы не должны сталкиваться с таймаутом соединения.
https://docs.djangoproject.com/en/1.10/ref/files/uploads/#writing-custom-upload-handlers
Я сделал несколько тестов, и он отлично работает в моем случае.
Вам нужно запустить новый multipart_upload с помощью boto, например, и постепенно отправлять куски.
Не забудьте проверить размер блока. 5Mb является минимальным, если ваш файл содержит более 1 части. (Ограничение S3)
Я думаю, что это лучшая альтернатива django-queued-storage, если вы действительно хотите загрузить непосредственно на s3 и избежать таймаута соединения.
Возможно, вам также понадобится создать собственное поле для правильного управления файлом, а не отправить его второй раз.
Ниже приведен пример с S3BotoStorage.
S3_MINIMUM_PART_SIZE = 5242880
class S3FileUploadHandler(FileUploadHandler):
chunk_size = setting('S3_FILE_UPLOAD_HANDLER_BUFFER_SIZE', S3_MINIMUM_PART_SIZE)
def __init__(self, request=None):
super(S3FileUploadHandler, self).__init__(request)
self.file = None
self.part_num = 1
self.last_chunk = None
self.multipart_upload = None
def new_file(self, field_name, file_name, content_type, content_length, charset=None, content_type_extra=None):
super(S3FileUploadHandler, self).new_file(field_name, file_name, content_type, content_length, charset, content_type_extra)
self.file_name = "{}_{}".format(uuid.uuid4(), file_name)
default_storage.bucket.new_key(self.file_name)
self.multipart_upload = default_storage.bucket.initiate_multipart_upload(self.file_name)
def receive_data_chunk(self, raw_data, start):
buffer_size = sys.getsizeof(raw_data)
if self.last_chunk:
file_part = self.last_chunk
if buffer_size < S3_MINIMUM_PART_SIZE:
file_part += raw_data
self.last_chunk = None
else:
self.last_chunk = raw_data
self.upload_part(part=file_part)
else:
self.last_chunk = raw_data
def upload_part(self, part):
self.multipart_upload.upload_part_from_file(
fp=StringIO(part),
part_num=self.part_num,
size=sys.getsizeof(part)
)
self.part_num += 1
def file_complete(self, file_size):
if self.last_chunk:
self.upload_part(part=self.last_chunk)
self.multipart_upload.complete_upload()
self.file = default_storage.open(self.file_name)
self.file.original_filename = self.original_filename
return self.file