Distribucija datotek iz Google Drive z uporabo nginx

prazgodovina

Zgodilo se je, da sem moral nekje shraniti več kot 1.5 TB podatkov in omogočiti tudi prenos navadnim uporabnikom prek neposredne povezave. Ker gre tradicionalno takšne količine pomnilnika za VDS, strošek najema katerega ni veliko vključen v proračun projekta iz kategorije "nič za početi", in iz izvornih podatkov sem imel VPS 400 GB SSD, kjer, tudi če sem hotel, nisem mogel dati 1.5 TB slik brez kompresije brez izgube bo uspelo.

In potem sem se spomnil, da če izbrišem smeti iz Google Drive, kot so programi, ki se bodo izvajali samo v sistemu Windows XP, in druge stvari, ki se premikajo iz ene naprave v drugo od časov, ko internet sploh ni bil tako hiter, ni neomejeno ( na primer, teh 10-20 različic virtualne škatle verjetno ne bo imelo druge vrednosti kot nostalgične), potem bi se moralo vse zelo dobro ujemati. Nič prej rečeno kot storjeno. In tako, ko smo prebili omejitev števila zahtev za api (mimogrede, tehnična podpora je brez kakršnih koli težav povečala kvoto zahtev na uporabnika na 100 v 10 sekundah), so podatki hitro pritekli do mesta nadaljnje uvedbe .

Zdi se, da je vse v redu, zdaj pa je treba to prenesti do končnega uporabnika. Poleg tega brez kakršnih koli preusmeritev na druge vire, vendar tako, da oseba preprosto pritisne gumb »Prenesi« in postane srečni lastnik dragocene datoteke.

Tukaj sem, pri bogu, zašel v vse mogoče težave. Sprva je bil skript v AmPHP, vendar nisem bil zadovoljen z obremenitvijo, ki jo je ustvaril (oster skok na začetku do 100-odstotne porabe jedra). Nato je prišel v poštev curl wrapper za ReactPHP, ki se je povsem ujemal z mojimi željami glede števila porabljenih ciklov procesorja, vendar ni dal hitrosti, ki sem jo želel (izkazalo se je, da lahko preprosto zmanjšate interval klica curl_multi_select, potem pa imamo požrešnost, podobno prvi možnosti ). Poskušal sem celo napisati majhno storitev v Rustu in je delovala precej hitro (presenetljivo je, da je delovalo glede na moje znanje), vendar sem želel več in nekako težko jo je bilo prilagoditi. Poleg tega so vse te rešitve nekako nenavadno medpomnile odziv in želel sem slediti trenutku, ko se je prenos datoteke končal z največjo natančnostjo.

Na splošno je bilo nekaj časa ukrivljeno, vendar je delovalo. Dokler se mi nekega dne ni porodila ideja, ki je bila izjemna v svoji norosti: nginx v teoriji lahko počne, kar hočem, deluje hitro in celo dopušča najrazličnejše perverzije s konfiguracijo. Moramo poskusiti - kaj če deluje? In po pol dneva vztrajnega iskanja se je rodila rešitev, ki je več mesecev stabilno delovala in izpolnjevala vse moje zahteve.

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

Kratko različico brez komentarjev si lahko ogledate pod spojlerjem

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 za upravljanje vse te sreče

Primer bo v PHP in namenoma napisan z minimalno opremo. Mislim, da bo vsak, ki ima izkušnje s katerim koli drugim jezikom, lahko integriral ta razdelek z uporabo mojega primera.

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

Na splošno je s to metodo precej enostavno organizirati distribucijo datotek uporabnikom iz katerega koli shrambe v oblaku. Da, tudi iz telegrama ali VK (pod pogojem, da velikost datoteke ne presega dovoljene velikosti tega pomnilnika). Imel sem podobno idejo to, vendar na žalost naletim na datoteke do 2 GB in še nisem našel metode ali modula za lepljenje odgovorov od zgoraj, pisanje nekakšnih ovojev za ta projekt pa je nerazumno delovno intenzivno.

Hvala za vašo pozornost. Upam, da vam je bila moja zgodba vsaj malo zanimiva ali koristna.

Vir: www.habr.com

Dodaj komentar