nginx аркылуу Google Drive'дан файлдарды таратуу

prehistory

Ошентип, мен бир жерде 1.5 ТБдан ашык маалыматты сактап, ошондой эле жөнөкөй колдонуучуларга аны түз шилтеме аркылуу жүктөп алуу мүмкүнчүлүгүн беришим керек болду. Салттуу түрдө эстутумдун мындай көлөмү VDSке кеткендиктен, анын ижара акысы долбоордун бюджетине "эч нерсе кыла албайт" категориясына кирбейт жана булактан алынган маалыматтар боюнча менде VPS 400GB SSD бар болчу, ал тургай, эгер мен кааласам, 1.5 ТБ сүрөттөрдү жоготуусуз кысуусуз коё албадым, ал ийгиликтүү болот.

Анан мен Google Drive'дан керексиз нерселерди жок кылсам, мисалы Windows XPде гана иштей турган программаларды жана Интернет мынчалык ылдам болбогон күндөрдөн бери бир түзмөктөн экинчисине көчүп келе жаткан башка нерселер чексиз эмес экенин эстедим ( мисалы, виртуалдык кутучанын ошол 10-20 версиясы ностальгиялыктан башка эч кандай мааниге ээ болушу күмөн болчу), анда баары абдан туура келиши керек. Жок эртеби кылганга караганда айтып коюу. Ошентип, api'ге суроо-талаптардын санынын чегин бузуп (айтмакчы, эч кандай көйгөйсүз техникалык колдоо 100 секунданын ичинде бир колдонуучуга суроо-талаптардын квотасын 10 000ге чейин көбөйттү), маалыматтар аны андан ары жайылтуу жерине тез агып кетти. .

Баары жакшы окшойт, бирок азыр аны акыркы колдонуучуга жеткирүү керек. Андан тышкары, башка ресурстарга эч кандай багыттоосуз, бирок адам жөн гана "Жүктөө" баскычын басып, баалуу файлдын бактылуу ээси болуп калат.

Мына, кудайга шугур, мен ар кандай кыйынчылыктарга кириптер болдум. Башында бул AmPHPдеги скрипт болчу, бирок мен аны жараткан жүктөмгө канааттанган жокмун (башында 100% негизги керектөө үчүн кескин секирүү). Андан кийин ReactPHP үчүн тармал орогуч ишке кирди, ал керектелүүчү CPU циклдеринин саны боюнча менин каалоолорума абдан туура келет, бирок мен каалагандай ылдамдыкты берген жок (сиз жөн гана чалуу аралыгын кыскарта аласыз. curl_multi_select, бирок анда бизде биринчи вариантка окшош суюктук бар ). Мен атүгүл Rust'та кичинекей кызмат жазууга аракет кылдым жана ал тез эле иштеди (менин билимимди эске алганда, ал иштегени таң калыштуу), бирок мен көбүрөөк нерсени кааладым жана аны ыңгайлаштыруу кандайдыр бир деңгээлде кыйын болду. Мындан тышкары, бул чечимдердин баары жоопту кандайдыр бир кызыктай буферлешти жана мен файлды жүктөө эң чоң тактык менен аяктаган учурга көз салгым келди.

Жалпысынан алганда, ал бир аз кыйшык болгон, бирок ал иштеген. Бир күнгө чейин мен өзүнүн жиндилиги менен укмуштуудай идеяны ойлоп таптым: nginx, теориялык жактан алганда, мен каалаган нерсени жасай алат, тез иштей алат жана конфигурация менен ар кандай бузукулуктарга жол берет. Биз аракет кылышыбыз керек - эгер ал иштеп калсачы? Ал эми жарым күндүк тынымсыз издөөдөн кийин бир нече ай бою туруктуу иштеген жана менин бардык талаптарыма жооп берген чечим пайда болду.

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

Комментарийсиз кыска версияны спойлердин астынан көрүүгө болот

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

Мына ушул бакыттын баарын башкаруу үчүн сценарий жазып жатабыз

Мисал PHPде болот жана атайын минималдуу комплект менен жазылган. Башка тилде тажрыйбасы бар адам менин мисалымды колдонуп, бул бөлүмдү бириктире алат деп ойлойм.

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

натыйжалары

Жалпысынан алганда, бул ыкма каалаган булут сактагычтан колдонуучуларга файлдарды бөлүштүрүүнү уюштурууну жеңилдетет. Ооба, ал тургай, телеграмма же VK, (файлдын өлчөмү бул сактагычтын уруксат берилген өлчөмүнөн ашпаган шартта). Менде окшош идея бар болчу бул, бирок, тилекке каршы, мен 2 ГБга чейинки файлдарды кезиктирем жана мен жогорудагы агымдан жоопторду жабыштыруу ыкмасын же модулун азырынча таба элекмин, жана бул долбоор үчүн кандайдыр бир оромолорду жазуу негизсиз эмгекти талап кылат.

Конул бурганын учун рахмат. Менин окуям сиз үчүн бир аз болсо да кызыктуу же пайдалуу болду деп үмүттөнөм.

Source: www.habr.com

Комментарий кошуу