Distribution af filer fra Google Drev ved hjælp af nginx

forhistorie

Det skete bare sådan, at jeg skulle gemme mere end 1.5 TB data et sted, og også give almindelige brugere mulighed for at downloade det via et direkte link. Da sådanne mængder hukommelse traditionelt går til VDS, er lejeudgifterne, som ikke er meget inkluderet i projektbudgettet fra kategorien "nothing to do", og fra kildedata, jeg havde en VPS 400GB SSD, hvor, selvom jeg ville, kunne jeg ikke sætte 1.5 TB billeder uden tabsfri komprimering, det vil lykkes.

Og så huskede jeg, at hvis jeg sletter junk fra Google Drev, som programmer, der kun kører på Windows XP, og andre ting, der har flyttet sig fra en enhed til en anden siden de dage, hvor internettet slet ikke var så hurtigt, ikke ubegrænset ( for eksempel var det usandsynligt, at de 10-20 versioner af den virtuelle boks havde nogen anden værdi end nostalgisk), så skulle alt passe meget godt. Ikke før sagt end gjort. Og så ved at bryde igennem grænsen for antallet af anmodninger til api'en (i øvrigt øgede teknisk support uden problemer kvoten af ​​anmodninger pr. bruger til 100 på 10 sekunder), flød dataene hurtigt til stedet for dens videre implementering .

Alt ser ud til at være godt, men nu skal det formidles til slutbrugeren. Desuden uden nogen omdirigeringer til andre ressourcer, men så en person blot trykker på knappen "Download" og bliver den lykkelige ejer af den skattede fil.

Her gik jeg ved gud i alle mulige problemer. Først var det et script i AmPHP, men jeg var ikke tilfreds med den belastning, det skabte (et kraftigt spring i starten til 100 % kerneforbrug). Så kom curl wrapperen til ReactPHP i spil, som ganske passede ind i mine ønsker i forhold til antallet af forbrugte CPU-cyklusser, men som slet ikke gav hastigheden, hvad jeg ønskede (det viste sig, at man blot kan reducere intervallet for opkald curl_multi_select, men så har vi samme frådseri som den første mulighed ). Jeg prøvede endda at skrive en lille tjeneste i Rust, og det virkede ret hurtigt (det er overraskende, at det virkede, givet min viden), men jeg ville have mere, og det var på en eller anden måde svært at tilpasse det. Derudover bufferlagrede alle disse løsninger på en eller anden mærkelig måde svaret, og jeg ønskede at spore det øjeblik, hvor filoverførslen sluttede med den største nøjagtighed.

Generelt var det skævt et stykke tid, men det virkede. Indtil jeg en dag kom med en idé, der var bemærkelsesværdig i sin skørhed: nginx kan i teorien gøre, hvad jeg vil, arbejde hurtigt og endda tillade alle mulige former for perversioner med konfiguration. Vi må prøve - hvad nu hvis det virker? Og efter en halv dags ihærdig søgen blev der født en løsning, som havde fungeret stabilt i flere måneder og levede op til alle mine krav.

Opsætning af 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 kort version uden kommentarer kan ses 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 er ved at skrive et manuskript til at styre al denne lykke

Eksemplet vil være i PHP og bevidst skrevet med et minimum af kit. Jeg tror, ​​at alle, der har erfaring med et hvilket som helst andet sprog, vil være i stand til at integrere dette afsnit ved hjælp af mit eksempel.

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

Resultaterne af

Generelt gør denne metode det ret nemt at organisere distributionen af ​​filer til brugere fra enhver cloud-lagring. Ja, selv fra telegram eller VK, (forudsat at filstørrelsen ikke overstiger den tilladte størrelse på dette lager). Jeg havde en ide, der ligner dette, men desværre støder jeg på filer op til 2GB, og jeg har endnu ikke fundet en metode eller et modul til at lime svar fra upstream, og det er urimeligt arbejdskrævende at skrive en form for wrappers til dette projekt.

Tak for din opmærksomhed. Jeg håber, at min historie i det mindste var en smule interessant eller nyttig for dig.

Kilde: www.habr.com

Tilføj en kommentar