Distribucija datoteka s Google diska pomoću nginxa

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 ovo, ali nažalost nailazim na datoteke do 2 GB, a još nisam pronašao metodu ili modul za lijepljenje odgovora odozgo, a pisanje neke vrste omotača za ovaj projekt nerazumno je zahtjevno.

Hvala vam na pažnji. Nadam se da vam je moja priča bila barem malo zanimljiva ili korisna.

Izvor: www.habr.com

Dodajte komentar