Ответ 1
Вы должны загрузить файл в 5MiB + кусках через S3 multipart API. Каждый из этих фрагментов требует Content-Length, но вы можете не загружать в память огромные объемы данных (100MiB +).
- Инициировать многоточечную загрузку S3.
- Собирайте данные в буфер до тех пор, пока этот буфер не достигнет нижнего предела размера блока S3 (5MiB). Генерируйте контрольную сумму MD5 при создании буфера.
- Загрузите этот буфер как часть, сохраните ETag (прочитайте документы на этом).
- Как только вы достигнете EOF своих данных, загрузите последний фрагмент (который может быть меньше 5MiB).
- Завершите загрузку многостраничных файлов.
S3 позволяет до 10000 частей. Таким образом, выбрав размер 5MiB, вы сможете загружать динамические файлы до 50GiB. Должно быть достаточно для большинства случаев использования.
Однако: если вам нужно больше, вам нужно увеличить размер детали. Либо используя более высокий размер детали (например, 10MiB), либо увеличивая его во время загрузки.
First 25 parts: 5MiB (total: 125MiB)
Next 25 parts: 10MiB (total: 375MiB)
Next 25 parts: 25MiB (total: 1GiB)
Next 25 parts: 50MiB (total: 2.25GiB)
After that: 100MiB
Это позволит вам загружать файлы размером до 1 ТБ (ограничение S3 для одного файла составляет 5 ТБ прямо сейчас), не теряя память без необходимости.
Заметка на ссылку на блог Sean O'Donnells:
Его проблема отличается от вашей - он знает и использует Content-Length перед загрузкой. Он хочет улучшить ситуацию: многие библиотеки обрабатывают загрузки, загружая все данные из файла в память. В псевдокоде, который будет примерно таким:
data = File.read(file_name)
request = new S3::PutFileRequest()
request.setHeader('Content-Length', data.size)
request.setBody(data)
request.send()
Его решение делает это, получая Content-Length
через файловую систему-API. Затем он передает данные с диска в поток запросов. В псевдокоде:
upload = new S3::PutFileRequestStream()
upload.writeHeader('Content-Length', File.getSize(file_name))
upload.flushHeader()
input = File.open(file_name, File::READONLY_FLAG)
while (data = input.read())
input.write(data)
end
upload.flush()
upload.close()