Distribuere filer fra Google Disk ved hjelp av nginx

forhistorie

Det skjedde at jeg trengte å lagre mer enn 1.5 TB med data et sted, og også gi vanlige brukere muligheten til å laste det ned via en direkte lenke. Siden tradisjonelt går slike mengder minne til VDS, hvis leiekostnad ikke er veldig mye inkludert i budsjettet til et prosjekt fra kategorien "ingenting å gjøre", og fra kildedataene hadde jeg en VPS 400GB SSD, hvor, til og med hvis jeg ville, kunne jeg ikke legge 1.5 TB med bilder uten tapsfri komprimering vil det lykkes.

Og så husket jeg at hvis jeg sletter søppel fra Google Drive, som programmer som bare kjører på Windows XP, og andre ting som har flyttet fra en enhet til en annen siden de dagene da Internett ikke var så raskt i det hele tatt, ikke ubegrenset ( for eksempel var det usannsynlig at de 10-20 versjonene av den virtuelle boksen hadde noen annen verdi enn nostalgisk), da burde alt passe veldig bra. Ikke før sagt enn gjort. Og så, ved å bryte gjennom grensen for antall forespørsler til api (forresten, teknisk støtte økte uten problemer kvoten av forespørsler per bruker til 100 10 på 000 sekunder), strømmet dataene raskt til stedet for videre distribusjon .

Alt ser ut til å være bra, men nå må det formidles til sluttbrukeren. Dessuten, uten noen omdirigeringer til andre ressurser, men slik at en person bare trykker på "Last ned" -knappen og blir den lykkelige eieren av den dyrebare filen.

Her gikk jeg ved gud i alle slags problemer. Først var det et script i AmPHP, men jeg var ikke fornøyd med belastningen det skapte (et kraftig hopp i starten til 100 % kjerneforbruk). Så kom krøllomslaget til ReactPHP inn i bildet, som passet ganske godt inn i mine ønsker når det gjelder antall forbrukte CPU-sykluser, men som ikke ga hastigheten i det hele tatt det jeg ønsket (det viste seg at du ganske enkelt kan redusere intervallet for å ringe curl_multi_select, men så har vi en fråtsing som ligner på det første alternativet ). Jeg prøvde til og med å skrive en liten tjeneste i Rust, og det fungerte ganske raskt (det er overraskende at det fungerte, gitt kunnskapen min), men jeg ville ha mer, og det var på en eller annen måte vanskelig å tilpasse det. I tillegg bufret alle disse løsningene på en merkelig måte responsen, og jeg ønsket å spore øyeblikket da filnedlastingen endte med størst nøyaktighet.

Generelt var det skjevt en stund, men det gikk. Helt til jeg en dag kom på en idé som var bemerkelsesverdig i sin galskap: nginx kan i teorien gjøre hva jeg vil, jobbe raskt og til og med tillate alle slags perversjoner med konfigurasjon. Vi må prøve – hva om det fungerer? Og etter en halv dag med iherdig leting ble det født en løsning som hadde fungert stabilt i flere måneder og møtte alle mine krav.

Sette opp 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;
}

En kortversjon uten kommentarer kan sees under spoileren

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;
}

Vi skriver et manus for å håndtere all denne lykken

Eksemplet vil være i PHP og bevisst skrevet med et minimum av kit. Jeg tror alle som har erfaring med et annet språk vil kunne integrere denne delen ved å bruke eksempelet mitt.

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

Resultater av

Generelt gjør denne metoden det ganske enkelt å organisere distribusjonen av filer til brukere fra hvilken som helst skylagring. Ja, selv fra telegram eller VK, (forutsatt at filstørrelsen ikke overskrider den tillatte størrelsen på denne lagringen). Jeg hadde en ide som ligner dette, men dessverre kommer jeg over filer på opptil 2 GB, og jeg har ennå ikke funnet en metode eller modul for å lime svar fra oppstrøms, og å skrive en slags innpakning for dette prosjektet er urimelig arbeidskrevende.

Takk for din oppmerksomhet. Jeg håper historien min var i det minste litt interessant eller nyttig for deg.

Kilde: www.habr.com

Legg til en kommentar