Verteilen von Dateien von Google Drive mit Nginx

Vorgeschichte

Zufällig musste ich irgendwo mehr als 1.5 TB Daten speichern und auch normalen Benutzern die Möglichkeit bieten, sie über einen direkten Link herunterzuladen. Da traditionell solche Mengen an Speicher an VDS gehen, sind die Mietkosten dafür nicht sehr stark im Budget eines Projekts aus der Kategorie „Nichts zu tun“ enthalten, und aus den Quelldaten hatte ich sogar eine VPS 400 GB SSD Wenn ich wollte, könnte ich nicht 1.5 TB Bilder ohne verlustfreie Komprimierung einfügen, es wird gelingen.

Und dann fiel mir ein, dass, wenn ich Müll von Google Drive lösche, wie Programme, die nur unter Windows XP laufen, und andere Dinge, die seit den Tagen, als das Internet noch nicht so schnell war, von einem Gerät auf ein anderes verschoben wurden, nicht unbegrenzt ( zum Beispiel dürften diese 10–20 Versionen der virtuellen Box kaum einen anderen Wert als Nostalgie haben), dann sollte alles sehr gut passen. Gesagt, getan. Nachdem die Grenze für die Anzahl der Anfragen an die API durchbrochen wurde (übrigens hat der technische Support die Anfragequote pro Benutzer problemlos auf 100 in 10 Sekunden erhöht), flossen die Daten schnell an den Ort ihrer weiteren Bereitstellung .

Alles scheint gut zu sein, aber jetzt muss es dem Endbenutzer vermittelt werden. Darüber hinaus ohne Weiterleitungen zu anderen Ressourcen, sondern so, dass eine Person einfach auf die Schaltfläche „Herunterladen“ klickt und der glückliche Besitzer der wertvollen Datei wird.

Hier, bei Gott, bin ich in alle möglichen Schwierigkeiten geraten. Zuerst war es ein Skript in AmPHP, aber ich war mit der dadurch erzeugten Last nicht zufrieden (ein starker Sprung zu Beginn auf 100 % Kernverbrauch). Dann kam der Curl-Wrapper für ReactPHP ins Spiel, der zwar hinsichtlich der Anzahl der verbrauchten CPU-Zyklen ganz meinen Wünschen entsprach, aber überhaupt nicht die Geschwindigkeit lieferte, die ich wollte (es stellte sich heraus, dass man das Intervall für Aufrufe einfach verkürzen kann). curl_multi_select, aber dann haben wir die gleiche Völlerei wie bei der ersten Option). Ich habe sogar versucht, einen kleinen Dienst in Rust zu schreiben, und es hat ziemlich schnell funktioniert (angesichts meines Wissens ist es überraschend, dass es funktioniert hat), aber ich wollte mehr und es war irgendwie schwierig, es anzupassen. Darüber hinaus haben alle diese Lösungen die Antwort seltsamerweise gepuffert, und ich wollte den Zeitpunkt, an dem der Dateidownload endete, mit größter Genauigkeit verfolgen.

Im Allgemeinen war es eine Zeit lang schief, aber es funktionierte. Bis ich eines Tages auf eine Idee kam, die in ihrer Verrücktheit bemerkenswert war: Nginx kann theoretisch tun, was ich will, schnell arbeiten und sogar alle möglichen Perversionen bei der Konfiguration zulassen. Wir müssen es versuchen – was ist, wenn es funktioniert? Und nach einem halben Tag hartnäckiger Suche war eine Lösung geboren, die seit mehreren Monaten stabil funktionierte und alle meine Anforderungen erfüllte.

NGINX einrichten

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

Eine Kurzfassung ohne Kommentare ist unter dem Spoiler zu sehen

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

Wir schreiben ein Drehbuch, um all dieses Glück zu bewältigen

Das Beispiel wird in PHP sein und bewusst mit einem Minimum an Kit geschrieben. Ich denke, jeder, der Erfahrung mit einer anderen Sprache hat, wird diesen Abschnitt anhand meines Beispiels integrieren können.

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

Ergebnisse

Im Allgemeinen ist es mit dieser Methode recht einfach, die Verteilung von Dateien an Benutzer aus jedem Cloud-Speicher zu organisieren. Ja, auch von Telegram oder VK, (vorausgesetzt, die Dateigröße überschreitet nicht die zulässige Größe dieses Speichers). Ich hatte eine ähnliche Idee diese, aber leider stoße ich auf Dateien mit bis zu 2 GB, und ich habe noch keine Methode oder kein Modul zum Kleben von Antworten von Upstreams gefunden, und das Schreiben einiger Wrapper für dieses Projekt ist unverhältnismäßig arbeitsintensiv.

Vielen Dank für Ihre Aufmerksamkeit. Ich hoffe, meine Geschichte war zumindest ein wenig interessant oder nützlich für Sie.

Source: habr.com

Kommentar hinzufügen