🇬🇧 English | 🇺🇦 Українська
5. Передавання файлів¶
Розділ про те, як плагін поводиться з великими файлами, як показати прогрес і як дати користувачеві кнопку «Скасувати».
Прогрес¶
Структура S3 Transfer Progress приходить у пін On Progress:
| Поле | Що це |
|---|---|
Bytes Transferred |
Скільки байтів пройшло |
Total Bytes |
Скільки очікується. 0, поки невідомо |
Percentage |
Від 0 до 100 |
Current Part / Total Parts |
Номер частини, для багаточастинних операцій |
bTotalKnown |
Чи можна вірити Total Bytes |
bTotalKnown існує не для краси. Чанкове зчитування дізнається розмір об'єкта лише з першої
відповіді, тож найперший виклик прогресу може прийти ще без нього. Поки bTotalKnown
дорівнює false, показуйте невизначений індикатор, а не смужку на нулі — інакше інтерфейс
виглядатиме зависшим.
Для смужки прогресу зручніше брати Get Progress Fraction із дескриптора: там уже
готове значення від 0 до 1.
Скасування¶
(гравець натиснув «Скасувати»)
│
└─► Cancel (на збереженому Transfer)
Що відбувається далі:
- Запит, який зараз у польоті, обривається — плагін не чекає, доки той завершиться сам.
- Багатокрокова операція не починає наступний крок.
- Спрацьовує пін On Failure із результатом
Cancelled.
Останнє важливе: скасування не «зникає тихо». Один із фінальних пінів спрацьовує завжди, тож очищення інтерфейсу не треба дублювати в двох місцях.
Про перервані (не скасовані) завантаження. Якщо процес просто зник — застосунок закрився, пропала мережа — частини лишаються в бакеті навмисно, щоб завантаження можна було продовжити. Щоб те, що ніхто не продовжив, не оплачувалося вічно, задайте бакету правило S3 Set Incomplete Upload Cleanup — див. «Правила життєвого циклу».
Скасування багаточастинного завантаження до того ж скасовує його в провайдера. Без цього вже надіслані частини лишилися б у бакеті: вони не видно в переліку об'єктів, але місце займають і оплачуються, доки їх не прибере правило життєвого циклу. Плагін робить це за вас.
Скасоване зчитування не лишає недописаного файлу за шляхом призначення. Наполовину
завантажений файл гірший за відсутній: для будь-якої перевірки «файл на місці?» він виглядає
успішним. У S3 Download File Chunked те, що встигло прийти, лишається поруч у .s3part,
щоб наступна спроба продовжила з цього місця — див.
«Відновлення перерваного зчитування».
Великі файли¶
Що відбувається автоматично¶
Усе, що більше за Multipart Part Size Bytes, стає багаточастинним завантаженням. Обирати
не треба — поріг єдиний і для файлів, і для масивів у пам'яті.
Порядок дій: плагін відкриває багаточастинне завантаження, надсилає частини (кілька одночасно), збирає їхні ідентифікатори й завершує завантаження. Якщо якась частина остаточно не пройшла, усе завантаження скасовується в провайдера — зібрати об'єкт із дірою однаково неможливо.
Пам'ять¶
S3 Upload File не читає файл у пам'ять. Він описує його, а далі:
- звичайне завантаження — транспорт стримить файл із диска;
- багаточастинне — кожна частина називає свій відрізок того самого файлу за зміщенням.
Тому витрата пам'яті пропорційна Max Concurrent Parts × Multipart Part Size Bytes, а не
розміру файлу. Завантаження на 2 ГБ із типовими налаштуваннями — це приблизно 20 МБ, а не
2 ГБ.
S3 Upload Bytes очевидно тримає масив у пам'яті — він там уже є.
Обмеження провайдера¶
S3 дозволяє не більше 10 000 частин. Якщо файл настільки великий, що за поточного розміру частини їх було б більше, плагін збільшує розмір частини сам і пише про це в лог. Провалити завантаження на останній частині було б гіршим варіантом.
Відновлення перерваного завантаження¶
Коли Resume Interrupted Uploads увімкнено (за замовчуванням так), наступна спроба
завантажити той самий файл питає провайдера, які частини вже на місці, і досилає лише
відсутні — замість того, щоб починати з нуля. Працює тільки для S3 Upload File (дані з
пам'яті не переживають перезапуск процесу) і тільки поки файл та розмір частини не змінилися
з моменту переривання. Докладно, з усіма межовими випадками — у
FAQ: «Чи можна відновити перерване завантаження?».
Зчитування великих файлів¶
S3 Download File¶
Пише у файл у міру надходження. Пам'ять постійна, прогрес побайтовий.
Це один потоковий запит — і саме тому перерване зчитування починається наново: щоб
продовжити з середини, потрібні діапазони. Якщо файл достатньо великий, щоб втрата на
половині мала значення, беріть S3 Download File Chunked.
S3 Download File Chunked¶
Зчитує серією діапазонних запитів — і тому вміє продовжувати перерване зчитування.
Chunk Size Bytes = 0 означає «взяти розмір частини з налаштувань».
Відновлення перерваного зчитування¶
Коли Resume Interrupted Downloads увімкнено (за замовчуванням так), S3 Download File
Chunked пише не одразу за призначенням, а у файл <файл>.s3part поруч. На місце
призначення він потрапляє одним рухом лише тоді, коли зчитаний повністю.
Це дає дві речі одразу:
- За шляхом призначення ніколи не лежить обрізаний файл. Наполовину завантажений файл гірший за відсутній: будь-яка перевірка «файл на місці?» вважає його успіхом.
- Те, що вже прийшло, не втрачається. Обрив зв'язку, тайм-аут, скасування користувачем —
.s3partлишається, і наступна спроба зчитати той самий об'єкт у той самий файл просить лише байти після нього.
Скільки вже зчитано, ніде окремо не записується — це розмір самого .s3part. Лічильник міг
би розійтися з дійсністю (запис ліг, процес помер, лічильник застарів), а файл із собою
розійтися не може.
Розмір шматка між спробами може бути іншим: діапазон починається з будь-якого байта, тож різати об'єкт однаково не обов'язково. Це відмінність від завантаження в сховище, де розмір частини змінювати не можна.
Чому це не «просто дописати з потрібного місця». Об'єкт міг змінитися між спробами. Тоді дописування пришило б хвіст нової версії до голови старої — і жодна пізніша перевірка цього б не помітила: розмір збігається, файл читається, а даних такого об'єкта ніколи не існувало.
Щоб цього не сталося, плагін запам'ятовує entity tag об'єкта (у Saved/S3/Downloads/) і
перевіряє версію на кожному діапазоні, трьома незалежними шарами:
| Перевірка | Що ловить |
|---|---|
Заголовок If-Match на кожному запиті |
Провайдер сам відмовляє (412) — до того, як чужі байти поїхали мережею |
| Порівняння ETag у відповіді | Провайдера, який прийняв умову, але не застосував її |
Порівняння розміру з Content-Range |
Провайдера, який на діапазон взагалі не віддає ETag |
Що відбувається, коли версія не збіглася, залежить від того, коли це виявлено:
- На першому ж діапазоні продовження — тобто в цьому запуску ще нічого не дописано:
.s3partвикидається, і об'єкт зчитується з нуля. Це звичайна ситуація «файл оновили відучора»; ви просили об'єкт — ви отримуєте його поточну версію цілою. - Посеред зчитування — об'єкт переписують просто зараз. Операція завершується з
Precondition Failed, а незавершений файл видаляється. Мовчки перезавантажити означало б оплатити трафік двічі без відома того, хто викликав, і без гарантії, що об'єкт не змінять знову.
Якщо провайдер не віддає ETag на діапазон, запис не створюється: зчитування працює, але відновити його не вийде — продовжувати наосліп означало б рівно ту саму склейку двох версій.
Ця перевірка версії працює завжди, а не лише під час продовження. Об'єкт, переписаний посеред звичайного чанкового зчитування, — така сама склейка, просто в межах однієї операції.
Побачити, що чекає на продовження, можна командою DemoS3ResumeRecords — див.
«Консоль розробника».
Одночасні передавання¶
Кілька операцій на одному клієнті працюють паралельно й не заважають одна одній. Кожна має власний дескриптор, власний прогрес і власний бюджет повторів.
Щоб зупинити все одразу:
Get S3 Subsystem → Cancel All Transfers
Дізнатися, чи щось іще виконується:
Get Default S3 Client → Get Active Transfer Count
Це зручно для екрана виходу з гри: не закривати застосунок, доки збереження не долетіло.
Повтори¶
Повторюються лише ті збої, від повтору яких є користь:
| Повторюється | Не повторюється |
|---|---|
| Обрив з'єднання, немає відповіді | 400 — некоректний запит |
| Таймаут | 401, 403 — проблема з ключами чи політикою |
| 429 — обмеження швидкості | 404 — немає такого об'єкта |
| 500, 502, 503, 504 | 409 — уже існує |
Затримка подвоюється з кожною спробою й береться випадково з проміжку від нуля до поточної стелі. Випадковість тут не косметична: коли провайдер обмежує швидкість пачці запитів, вони падають одночасно — і без розкидання повернулися б теж одночасно, відтворивши ту саму пачку.
У результаті операції є поле Retry Count — скільки повторів було витрачено. Ненульове
значення на успішній операції варто логувати: воно говорить про якість каналу або про те,
що ви близько до ліміту провайдера.
Кілька байтів про потоки¶
Усі зворотні виклики приходять в ігровому потоці. Це гарантує транспортний шар плагіна, а не кожне місце виклику окремо, тож із будь-якого піна можна безпечно чіпати актори, віджети й UObject-и без жодного маршалінгу.
Далі: 6. C++ API