Distribuirea fișierelor din Google Drive folosind nginx

preistorie

S-a întâmplat că trebuia să stochez mai mult de 1.5 TB de date undeva și, de asemenea, să ofer posibilitatea utilizatorilor obișnuiți de a le descărca printr-un link direct. Întrucât în ​​mod tradițional astfel de cantități de memorie merg la VDS, costul închirierii care nu este foarte mult inclus în bugetul proiectului din categoria „nimic de făcut”, iar din datele sursă aveam un SSD VPS de 400 GB, unde, chiar dacă am am vrut, nu am putut pune 1.5TB de imagini fără compresie fără pierderi, va reuși.

Și apoi mi-am amintit că dacă șterg junk-ul de pe Google Drive, cum ar fi programe care vor rula doar pe Windows XP și alte lucruri care s-au mutat de la un dispozitiv la altul din zilele în care Internetul nu era deloc atât de rapid, nici nelimitat ( de exemplu, acele 10-20 de versiuni ale cutiei virtuale erau puțin probabil să aibă altă valoare decât nostalgică), atunci totul ar trebui să se potrivească foarte bine. Făcut repede şi foarte bine. Și astfel, depășind limita numărului de solicitări către API (apropo, asistența tehnică a crescut fără probleme cota de solicitări per utilizator la 100 în 10 de secunde), datele au trecut rapid la locul implementării sale ulterioare. .

Totul pare să fie bine, dar acum trebuie să fie transmis utilizatorului final. Mai mult decât atât, fără redirecționări către alte resurse, dar astfel încât o persoană să apese pur și simplu butonul „Descărcare” și să devină fericitul proprietar al fișierului prețuit.

Aici, Doamne, am intrat în tot felul de necazuri. La început a fost un script în AmPHP, dar nu am fost mulțumit de încărcarea pe care a creat-o (un salt brusc la început la consumul de bază 100%). Apoi a intrat în joc curl wrapper-ul pentru ReactPHP, care se potrivea destul de mult cu dorințele mele în ceea ce privește numărul de cicluri CPU consumate, dar nu a oferit viteza deloc ceea ce mi-am dorit (s-a dovedit că puteți reduce pur și simplu intervalul de apelare curl_multi_select, dar apoi avem o lăcomie similară cu prima opțiune). Am încercat chiar să scriu un mic serviciu în Rust și a funcționat destul de repede (este surprinzător că a funcționat, având în vedere cunoștințele mele), dar am vrut mai mult și a fost cumva dificil să-l personalizez. În plus, toate aceste soluții au tamponat într-un fel ciudat răspunsul și am vrut să urmăresc momentul în care descărcarea fișierului s-a încheiat cu cea mai mare acuratețe.

În general, a fost strâmb o vreme, dar a funcționat. Până când într-o zi mi-a venit o idee remarcabilă prin nebunia ei: nginx, în teorie, poate face ce vreau, să lucreze rapid și chiar să permită tot felul de perversiuni cu configurație. Trebuie să încercăm - și dacă funcționează? Și după o jumătate de zi de căutări persistente, s-a născut o soluție care funcționa stabil de câteva luni și răspundea tuturor cerințelor mele.

Configurarea NGINX

# Первым делом создадим в конфигах нашего сайта отдельную локацию.
location ~* ^/google_drive/(.+)$ {

    # И закроем её от посторонних глаз (рук, ног и прочих частей тела).
    internal;

    # Ограничим пользователям скорость до разумных пределов (я за равноправие).
    limit_rate 1m;

    # А чтоб nginx мог найти сервера google drive укажем ему адрес резолвера.
    resolver 8.8.8.8;

    # Cоберем путь к нашему файлу (мы потом передадим его заголовками).
    set $download_url https://www.googleapis.com/drive/v3/files/$upstream_http_file_id?alt=media;

    # А так же Content-Disposition заголовок, имя файла мы передадим опять же в заголовках.
    set $content_disposition 'attachment; filename="$upstream_http_filename"';

    # Запретим буфферизировать ответ на диск.
    proxy_max_temp_file_size 0;

    # И, что немаловажно, передадим заголовок с токеном (не знаю почему, но в заголовках из $http_upstream токен передать не получилось. Вернее передать получилось, но скорей всего его где-то нужно экранировать, потому что гугл отдает ошибку авторизации).
    proxy_set_header Authorization 'Bearer $1';

    # И все, осталось отправить запрос гуглу по ранее собранному нами адресу.
    proxy_pass $download_url;

    # А чтоб у пользователя при скачивании отобразилось правильное имя файла мы добавим соответствующий заголовок.
    add_header Content-Disposition $content_disposition;

    # Опционально можно поубирать ненужные нам заголовки от гугла.
    proxy_hide_header Content-Disposition;
    proxy_hide_header Alt-Svc;
    proxy_hide_header Expires;
    proxy_hide_header Cache-Control;
    proxy_hide_header Vary;
    proxy_hide_header X-Goog-Hash;
    proxy_hide_header X-GUploader-UploadID;
}

O versiune scurtă fără comentarii poate fi văzută sub spoiler

location ~* ^/google_drive/(.+)$ {
    internal;
    limit_rate 1m;
    resolver 8.8.8.8;
    
    set $download_url https://www.googleapis.com/drive/v3/files/$upstream_http_file_id?alt=media;
    set $content_disposition 'attachment; filename="$upstream_http_filename"';
    
    proxy_max_temp_file_size 0;
    proxy_set_header Authorization 'Bearer $1';
    proxy_pass $download_url;
    
    add_header Content-Disposition $content_disposition;
    
    proxy_hide_header Content-Disposition;
    proxy_hide_header Alt-Svc;
    proxy_hide_header Expires;
    proxy_hide_header Cache-Control;
    proxy_hide_header Vary;
    proxy_hide_header X-Goog-Hash;
    proxy_hide_header X-GUploader-UploadID;
}

Scriem un scenariu pentru a gestiona toată această fericire

Exemplul va fi în PHP și scris în mod deliberat cu un minim de kit. Cred că oricine are experiență cu orice altă limbă va putea integra această secțiune folosind exemplul meu.

<?php

# Токен для Google Drive Api.
define('TOKEN', '*****');

# ID файла на гугл диске
$fileId = 'abcdefghijklmnopqrstuvwxyz1234567890';

# Опционально, но так как мы не передаем никаких данных - почему бы и нет?
http_response_code(204);

# Зададим заголовок c ID файла (в конфигах nginx мы потом получим его как $upstream_http_file_id).
header('File-Id: ' . $fileId);
# И заголовок с именем файла (соответственно $upstream_http_filename).
header('Filename: ' . 'test.zip');
# Внутренний редирект. А еще в адресе мы передадим токен, тот самый, что мы получаем из $1 в nginx.
header('X-Accel-Redirect: ' . rawurlencode('/google_drive/' . TOKEN));

Rezultatele

În general, această metodă facilitează organizarea distribuției fișierelor către utilizatori din orice stocare în cloud. Da, chiar și din telegramă sau VK, (cu condiția ca dimensiunea fișierului să nu depășească dimensiunea permisă a acestei stocări). Am avut o idee asemănătoare cu acest, dar, din păcate, dau peste fișiere de până la 2 GB și nu am găsit încă o metodă sau un modul pentru lipirea răspunsurilor din amonte, iar scrierea unor tipuri de învelișuri pentru acest proiect necesită o muncă nerezonabilă.

Vă mulțumim pentru atenție. Sper că povestea mea a fost măcar puțin interesantă sau utilă pentru tine.

Sursa: www.habr.com

Adauga un comentariu