Mendistribusikan file dari Google Drive menggunakan nginx

prasejarah

Kebetulan saya perlu menyimpan lebih dari 1.5 TB data di suatu tempat, dan juga memberikan kemampuan bagi pengguna biasa untuk mengunduhnya melalui tautan langsung. Karena secara tradisional jumlah memori seperti itu digunakan untuk VDS, biaya sewa yang tidak terlalu banyak termasuk dalam anggaran proyek dari kategori “tidak ada hubungannya”, dan dari data sumber saya memiliki VPS 400GB SSD, di mana, meskipun saya ingin, saya tidak dapat menempatkan gambar 1.5TB tanpa kompresi lossless, itu akan berhasil.

Dan kemudian saya teringat jika saya menghapus sampah dari Google Drive, seperti program yang hanya akan berjalan di Windows XP, dan hal-hal lain yang berpindah dari satu perangkat ke perangkat lainnya sejak zaman ketika Internet tidak begitu cepat sama sekali tidak tidak terbatas ( misalnya, versi 10-20 kotak virtual tersebut sepertinya tidak memiliki nilai apa pun selain nostalgia), maka semuanya akan sangat cocok. Tidak lama setelah diucapkan, dilakukan. Jadi, dengan menembus batas jumlah permintaan ke api (omong-omong, dukungan teknis meningkatkan kuota permintaan per pengguna menjadi 100 dalam 10 detik tanpa masalah), data dengan cepat mengalir ke tempat penerapan lebih lanjut. .

Segalanya tampak baik-baik saja, tetapi sekarang perlu disampaikan kepada pengguna akhir. Selain itu, tanpa pengalihan apa pun ke sumber daya lain, tetapi seseorang cukup menekan tombol "Unduh" dan menjadi pemilik bahagia dari file berharga tersebut.

Di sini, demi Tuhan, saya mengalami berbagai macam masalah. Awalnya ini adalah skrip di AmPHP, tapi saya tidak puas dengan beban yang dibuatnya (lompatan tajam di awal hingga konsumsi inti 100%). Kemudian curl wrapper untuk ReactPHP mulai berlaku, yang sesuai dengan keinginan saya dalam hal jumlah siklus CPU yang dikonsumsi, tetapi tidak memberikan kecepatan sama sekali yang saya inginkan (ternyata Anda cukup mengurangi interval panggilan curl_multi_select, tapi kemudian kita memiliki kerakusan yang mirip dengan opsi pertama). Saya bahkan mencoba menulis layanan kecil di Rust, dan itu bekerja cukup cepat (mengejutkan bahwa itu berhasil, mengingat pengetahuan saya), tetapi saya menginginkan lebih, dan entah bagaimana sulit untuk menyesuaikannya. Selain itu, semua solusi ini entah bagaimana secara aneh mem-buffer respons, dan saya ingin melacak momen ketika pengunduhan file berakhir dengan akurasi paling tinggi.

Secara umum, itu bengkok untuk sementara waktu, tetapi berhasil. Hingga suatu hari saya mendapat ide yang luar biasa dalam kegilaannya: nginx, secara teori, dapat melakukan apa yang saya inginkan, bekerja dengan cepat, dan bahkan mengizinkan segala macam penyimpangan dengan konfigurasi. Kita harus mencobanya - bagaimana jika berhasil? Dan setelah setengah hari pencarian yang gigih, lahirlah solusi yang telah bekerja secara stabil selama beberapa bulan dan memenuhi semua kebutuhan saya.

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

Versi pendek tanpa komentar dapat dilihat di bawah 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;
}

Kami sedang menulis naskah untuk mengatur semua kebahagiaan ini

Contohnya akan dalam PHP dan sengaja ditulis dengan kit yang minimal. Saya rasa siapa pun yang memiliki pengalaman dengan bahasa lain akan dapat mengintegrasikan bagian ini menggunakan contoh saya.

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

Hasil

Secara umum, metode ini cukup memudahkan pengorganisasian distribusi file ke pengguna dari penyimpanan cloud mana pun. Ya, bahkan dari telegram atau VK, (asalkan ukuran file tidak melebihi ukuran penyimpanan yang diizinkan). Saya punya ide serupa dengan ini, tapi sayangnya saya menemukan file hingga 2GB, dan saya belum menemukan metode atau modul untuk merekatkan respons dari hulu, dan menulis semacam pembungkus untuk proyek ini memakan banyak tenaga kerja.

Terima kasih atas perhatian Anda. Saya harap cerita saya setidaknya sedikit menarik atau bermanfaat bagi Anda.

Sumber: www.habr.com

Tambah komentar