Compartint fitxers de Google Drive amb nginx

prehistòria

Va donar la casualitat que necessitava emmagatzemar més d'1.5 TB de dades en algun lloc i també oferir la possibilitat que els usuaris normals les baixessin mitjançant un enllaç directe. Com que tradicionalment aquestes quantitats de memòria van a VDS, el cost del lloguer no està molt inclòs en el pressupost del projecte de la categoria "res a fer", i de les dades d'origen tenia un SSD VPS de 400 GB, on, encara que jo volia, no podria posar 1.5 TB d'imatges sense compressió sense pèrdues, tindrà èxit.

I aleshores vaig recordar que si esborro la brossa de Google Drive, com ara programes que només s'executaran a Windows XP, i altres coses que s'han anat passant de mitjans a mitjans des dels dies en què Internet no era tan ràpid en absolut no il·limitat (per Per exemple, aquelles 10-20 versions de la caixa virtual probablement no tinguessin cap valor que no fos nostàlgic), aleshores tot hauria d'encaixar molt bé. No més aviat dir que fet. I així, superant el límit del nombre de sol·licituds a l'API (per cert, el suport tècnic va augmentar sense cap problema la quota de sol·licituds per usuari a 100 en 10 segons), les dades van fluir ràpidament al lloc del seu posterior desplegament. .

Sembla que tot va bé, però ara s'ha de transmetre a l'usuari final. A més, sense cap redireccionament a altres recursos, però de manera que una persona simplement prem el botó "Descarrega" i es converteixi en el feliç propietari del fitxer atresorat.

Aquí, per Déu, vaig entrar en tota mena de problemes. Al principi era un script en AmPHP, però no estava satisfet amb la càrrega que creava (un salt brusc al principi fins al 100% del consum del nucli). Llavors va entrar en joc l'embolcall curl per a ReactPHP, que s'adaptava bastant als meus desitjos pel que fa al nombre de cicles de CPU consumits, però no va donar la velocitat gens del que volia (va resultar que simplement podeu reduir l'interval de trucades). curl_multi_select, però aleshores tenim una gula semblant a la primera opció). Fins i tot vaig intentar escriure un petit servei a Rust i va funcionar força ràpidament (és sorprenent que funcionés, tenint en compte els meus coneixements), però en volia més, i d'alguna manera va ser difícil personalitzar-lo. A més, totes aquestes solucions d'alguna manera estranyament emmagatzemaven la resposta, i volia fer un seguiment del moment en què la descàrrega del fitxer va acabar amb la màxima precisió.

En general, va estar tort durant un temps, però va funcionar. Fins que un dia se'm va ocórrer una idea que era notable per la seva bogeria: nginx, en teoria, pot fer el que vull, treballar ràpidament i fins i tot permetre tot tipus de perversions amb configuració. Ho hem de provar, i si funciona? I després de mig dia de recerca persistent, va néixer una solució que feia diversos mesos que funcionava de manera estable i que complia tots els meus requisits.

Configuració de 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;
}

Una versió curta sense comentaris es pot veure sota l'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;
}

Estem escrivint un guió per gestionar tota aquesta felicitat

L'exemple estarà en PHP i escrit deliberadament amb un mínim de kit. Crec que qualsevol persona que tingui experiència amb qualsevol altre idioma podrà integrar aquesta secció utilitzant el meu exemple.

<?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));

Resultats de

En general, aquest mètode fa que sigui bastant fàcil organitzar la distribució de fitxers als usuaris des de qualsevol emmagatzematge al núvol. Sí, fins i tot des de telegram o VK, (sempre que la mida del fitxer no superi la mida permesa d'aquest emmagatzematge). Vaig tenir una idea semblant a això, però malauradament em trobo amb fitxers de fins a 2 GB, i encara no he trobat un mètode o mòdul per enganxar les respostes des de l'aigua amunt, i escriure algun tipus d'embolcalls per a aquest projecte requereix una feina irracional.

Gràcies per la vostra atenció. Espero que la meva història us hagi estat almenys una mica interessant o útil.

Font: www.habr.com

Afegeix comentari