Distribution de fichiers depuis Google Drive à l'aide de nginx

Préhistoire

Il se trouve que j'avais besoin de stocker plus de 1.5 To de données quelque part et de permettre aux utilisateurs ordinaires de les télécharger via un lien direct. Étant donné que traditionnellement, de telles quantités de mémoire vont au VDS, le coût de location, qui n'est pas très inclus dans le budget du projet, de la catégorie « rien à faire », et à partir des données sources, j'avais un SSD VPS de 400 Go, où, même si je je le voulais, je ne pouvais pas mettre 1.5 To d'images sans compression sans perte, ça réussira.

Et puis je me suis souvenu que si je supprimais les fichiers indésirables de Google Drive, comme les programmes qui ne fonctionnaient que sous Windows XP, et d'autres choses qui se déplaçaient d'un appareil à un autre depuis l'époque où Internet n'était pas si rapide du tout, pas illimité ( par exemple, il est peu probable que ces 10 à 20 versions de la boîte virtuelle aient une valeur autre que nostalgique), alors tout devrait très bien s'adapter. À peine dit que c'était fait. Ainsi, en dépassant la limite du nombre de requêtes vers l'API (d'ailleurs, le support technique a augmenté sans aucun problème le quota de requêtes par utilisateur à 100 10 en 000 secondes), les données ont rapidement afflué vers le lieu de leur déploiement ultérieur. .

Tout semble aller bien, mais il faut maintenant que cela soit transmis à l'utilisateur final. De plus, sans aucune redirection vers d'autres ressources, mais pour qu'une personne appuie simplement sur le bouton « Télécharger » et devienne l'heureux propriétaire du fichier précieux.

Ici, par Dieu, j'ai eu toutes sortes de problèmes. Au début c'était un script en AmPHP, mais je n'étais pas satisfait de la charge qu'il créait (un bond brutal au départ jusqu'à 100% de consommation core). Ensuite, le wrapper curl pour ReactPHP est entré en jeu, qui correspondait tout à fait à mes souhaits en termes de nombre de cycles CPU consommés, mais ne donnait pas du tout la vitesse que je voulais (il s'est avéré que vous pouvez simplement réduire l'intervalle d'appel curl_multi_select, mais nous avons alors une gourmandise similaire à la première option ). J'ai même essayé d'écrire un petit service dans Rust, et cela a fonctionné assez rapidement (c'est surprenant que cela ait fonctionné, compte tenu de mes connaissances), mais j'en voulais plus, et c'était en quelque sorte difficile de le personnaliser. De plus, toutes ces solutions tamponnaient étrangement la réponse, et je voulais suivre le moment où le téléchargement du fichier se terminait avec la plus grande précision.

En général, c'était tordu pendant un moment, mais ça a fonctionné. Jusqu'au jour où j'ai eu une idée remarquable par sa folie : nginx, en théorie, peut faire ce que je veux, travailler rapidement et même permettre toutes sortes de perversions avec la configuration. Nous devons essayer – et si cela fonctionnait ? Et après une demi-journée de recherche persistante, est née une solution qui fonctionnait de manière stable depuis plusieurs mois et répondait à toutes mes exigences.

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

Une version courte sans commentaires est visible sous le spoiler

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

Nous écrivons un scénario pour gérer tout ce bonheur

L'exemple sera en PHP et volontairement écrit avec un minimum de kit. Je pense que toute personne ayant de l'expérience avec une autre langue pourra intégrer cette section en utilisant mon exemple.

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

Les résultats de

En général, cette méthode permet d'organiser assez facilement la distribution de fichiers aux utilisateurs à partir de n'importe quel stockage cloud. Oui, même à partir d'un télégramme ou de VK (à condition que la taille du fichier ne dépasse pas la taille autorisée pour ce stockage). J'ai eu une idée similaire à cette, mais malheureusement, je tombe sur des fichiers allant jusqu'à 2 Go, et je n'ai pas encore trouvé de méthode ou de module pour coller les réponses en amont, et écrire une sorte de wrappers pour ce projet demande trop de travail.

Merci pour votre attention. J'espère que mon histoire vous a été au moins un peu intéressante ou utile.

Source: habr.com

Ajouter un commentaire