Nginx kullanarak Google Drive'dan dosya dağıtma

tarih öncesi

Öyle oldu ki, bir yerde 1.5 TB'tan fazla veri depolamam ve ayrıca sıradan kullanıcılara bunları doğrudan bağlantı yoluyla indirme olanağı sağlamam gerekiyordu. Geleneksel olarak bu kadar miktarda bellek VDS'ye gittiğinden, "yapılacak bir şey yok" kategorisinden proje bütçesine pek dahil edilmeyen kiralama maliyeti ve kaynak verilerden, VPS 400GB SSD'ye sahiptim; istedim, 1.5 TB'lık görüntüleri kayıpsız sıkıştırma olmadan koyamadım, başarılı olacak.

Ve sonra, Google Drive'dan yalnızca Windows XP'de çalışacak programlar ve İnternet'in o kadar hızlı olmadığı günlerden beri sınırsız olmadığı günlerden beri bir cihazdan diğerine taşınan diğer şeyler gibi önemsiz şeyleri silersem hatırladım ( örneğin, sanal kutunun bu 10-20 versiyonunun nostaljik olmaktan başka bir değere sahip olması pek mümkün değildi), o zaman her şey çok iyi uyum sağlamalı. Daha erken olmaz dedi ve bitirdi. Ve böylece, API'ye yapılan istek sayısı sınırını aşarak (bu arada, teknik destek sorunsuz bir şekilde kullanıcı başına istek kotasını 100 saniyede 10'e çıkardı), veriler hızla daha sonraki dağıtım yerine aktı .

Her şey yolunda gibi görünüyor ama artık bunun son kullanıcıya aktarılması gerekiyor. Üstelik, diğer kaynaklara herhangi bir yönlendirme olmadan, kişinin yalnızca "İndir" düğmesine basması ve değerli dosyanın mutlu sahibi olması için.

Burada Allah'a şükür her türlü sıkıntıya girdim. Başlangıçta AmPHP'de bir betikti ama yarattığı yükten memnun değildim (%100 çekirdek tüketimine doğru başlangıçta keskin bir sıçrama). Daha sonra, tüketilen CPU döngüsü sayısı açısından isteklerime oldukça uyan, ancak hızı hiç istediğimi vermeyen ReactPHP için kıvrılma sarmalayıcı devreye girdi (görünüşe göre arama aralığını basitçe azaltabilirsiniz) curl_multi_select, ancak ilk seçeneğe benzer bir oburlukla karşı karşıyayız). Rust'ta küçük bir hizmet yazmayı bile denedim ve oldukça hızlı çalıştı (bilgim göz önüne alındığında işe yaraması şaşırtıcıydı), ancak daha fazlasını istedim ve onu özelleştirmek bir şekilde zordu. Ek olarak, tüm bu çözümler bir şekilde yanıtı garip bir şekilde ara belleğe aldı ve dosya indirme işleminin sona erdiği anı en yüksek doğrulukla izlemek istedim.

Genel olarak bir süre çarpıktı ama işe yaradı. Ta ki bir gün aklıma çılgınlığıyla dikkat çeken bir fikir geldi: Nginx teoride istediğimi yapabiliyor, hızlı çalışabiliyor ve hatta konfigürasyonuyla her türlü sapkınlığa izin veriyor. Denememiz lazım; ya işe yararsa? Yarım gün süren ısrarlı aramanın ardından, birkaç aydır istikrarlı bir şekilde çalışan ve tüm gereksinimlerimi karşılayan bir çözüm doğdu.

NGINX'i ayarlama

# Первым делом создадим в конфигах нашего сайта отдельную локацию.
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;
}

Yorumsuz kısa versiyonu spoiler altında görebilirsiniz

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

Tüm bu mutluluğu yönetmek için senaryo yazıyoruz

Örnek PHP'de olacak ve minimum kit ile kasıtlı olarak yazılacaktır. Başka bir dille deneyimi olan herkesin benim örneğimi kullanarak bu bölümü entegre edebileceğini düşünüyorum.

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

sonuçlar

Genel olarak bu yöntem, dosyaların herhangi bir bulut depolama alanından kullanıcılara dağıtımını düzenlemeyi oldukça kolaylaştırır. Evet, telgraf veya VK'dan bile (dosya boyutunun bu depolamanın izin verilen boyutunu aşmaması koşuluyla). buna benzer bir fikrim vardı bu, ancak ne yazık ki 2 GB'a kadar dosyalarla karşılaşıyorum ve henüz yukarı yöndeki yanıtları yapıştırmak için bir yöntem veya modül bulamadım ve bu proje için bir tür sarmalayıcı yazmak mantıksız derecede emek yoğun.

İlginiz için teşekkür ederiz. Umarım hikayem sizin için en azından biraz ilginç veya faydalı olmuştur.

Kaynak: habr.com

Yorum ekle