prapovijest
Slučajno se dogodilo da sam trebao negdje pohraniti više od 1.5 TB podataka, a također i omogućiti običnim korisnicima da ih preuzimaju putem izravne veze. Budući da tradicionalno takve količine memorije idu na VDS, trošak najma koji nije previše uključen u proračun projekta iz kategorije "nema posla", a prema izvornim podacima imao sam VPS 400GB SSD, gdje, čak i ako sam htio, nisam mogao staviti 1.5TB slika bez lossless kompresije uspjet će.
A onda sam se sjetio da ako izbrišem smeće s Google diska, poput programa koji će raditi samo na Windows XP-u, i drugih stvari koje se sele s jednog uređaja na drugi još od dana kada Internet nije bio tako brz, uopće nisu neograničene ( na primjer, tih 10-20 verzija virtualne kutije vjerojatno nije imalo nikakvu vrijednost osim nostalgične), onda bi sve trebalo jako dobro pristajati. Rečeno, učinjeno. I tako, probijajući ograničenje broja zahtjeva za api (usput, tehnička podrška je bez ikakvih problema povećala kvotu zahtjeva po korisniku na 100 u 10 sekundi), podaci su brzo tekli na mjesto svoje daljnje implementacije .
Čini se da je sve dobro, ali sada to treba prenijeti do krajnjeg korisnika. Štoviše, bez ikakvih preusmjeravanja na druge resurse, ali tako da osoba jednostavno pritisne gumb "Preuzmi" i postane sretni vlasnik dragocjene datoteke.
Eto, bogami, zapao sam u svakakve nevolje. U početku je to bila skripta u AmPHP-u, ali nisam bio zadovoljan opterećenjem koje je stvorilo (oštar skok na početku do 100% potrošnje jezgre). Zatim je na scenu stupio curl wrapper za ReactPHP, koji se sasvim uklopio u moje želje u pogledu broja potrošenih CPU ciklusa, ali nije dao brzinu kakvu sam želio (pokazalo se da jednostavno možete smanjiti interval pozivanja curl_multi_select, ali tada imamo proždrljivost sličnu prvoj opciji ). Čak sam pokušao napisati malu uslugu u Rustu i radila je prilično brzo (iznenađujuće je da je uspjela, s obzirom na moje znanje), ali sam želio više i bilo je nekako teško prilagoditi je. Osim toga, sva su ta rješenja na neki čudan način buferirala odgovor, a ja sam htio s najvećom točnošću pratiti trenutak kada je preuzimanje datoteke završilo.
Općenito, neko je vrijeme bilo krivo, ali je uspjelo. Sve dok jednog dana nisam došao na ideju koja je bila izvanredna u svojoj ludosti: nginx, u teoriji, može raditi što hoću, raditi brzo, pa čak i dopustiti svakakve perverzije s konfiguracijom. Moramo pokušati - što ako uspije? I nakon pola dana uporne potrage rodilo se rješenje koje je već nekoliko mjeseci stabilno radilo i ispunjavalo sve moje zahtjeve.
Postavljanje NGINX-a
# Первым делом создадим в конфигах нашего сайта отдельную локацию.
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;
}
Kratka verzija bez komentara može se vidjeti ispod spojlera
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;
}
Pišemo scenarij kako bismo upravljali svom tom srećom
Primjer će biti u PHP-u i namjerno napisan s minimalnom opremom. Mislim da će svatko tko ima iskustva s bilo kojim drugim jezikom moći integrirati ovaj odjeljak koristeći se mojim primjerom.
<?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));
Rezultati
Općenito, ova metoda olakšava organiziranje distribucije datoteka korisnicima iz bilo koje pohrane u oblaku. Da, čak i iz telegrama ili VK, (pod uvjetom da veličina datoteke ne prelazi dopuštenu veličinu ove pohrane). Imao sam sličnu ideju
Hvala vam na pažnji. Nadam se da vam je moja priča bila barem malo zanimljiva ili korisna.
Izvor: www.habr.com