- PVSM.RU - https://www.pvsm.ru -
В ходе разработки серверной части сервиса загрузки файлов на Golang родилось отдельное приложение – pavo [1]. В задачи приложения входит загрузка целых файлов, по одному или несколько за раз, кусочная загрузка файла(chunked upload), конвертер изображений. Реализована загрузка данных через multipart/form-data
и загрузка файла в бинарном виде в теле запроса. Для работы в production окружении используется nginx для авторизации и обработки медленных соединений. В качестве клиентской библиотеки можно использовать jQuery File Uploader [2].
Для установки приложения необходим компилятор Golang. Инструкцию по его установке можно найти на официальном сайте [3]. Так же необходимо настроить [4] переменную окружения $GOPATH
.
Для примера, как это можно сделать в MacOS:
$ brew install go
$ mkdir $HOME/go
# Add this line in your .zshrc or .bash_profile
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
Репозитории с кодом в сообществе не централизованы. Используются различные системы контроля версий:
Пример установки в MacOS:
$ brew install git mercurial svn bazaar
Для конвертации изображений на сервер используется ImageMagick [9]:
$ brew install imagemagick
При первой установке запустите команду в консоли:
$ go get github.com/kavkaz/pavo
При обновлении приложения и зависимых библиотек:
$ go get -u github.com/kavkaz/pavo/...
Для того, чтобы посмотреть, как работает приложение с базовым примером, запустим в консоли команду:
$ pavo --storage=$GOPATH/src/github.com/kavkaz/pavo/dummy/root_storage
Тем самым мы запустили приложение с корневой директорией в указанном через опцию --storage
каталоге. Сервис c базовым примером будет доступен по адресу http://localhost:9073/example/jfu-basic.html
. Для указания другого хоста и порта используйте консольную опцию --host
.
Типовой ответ сервера при загрузке изображения:
{
"files": [
{
"dir": "/image/2014/6s/1c5cnx",
"name": "original_user_filename.jpg",
"type": "image",
"versions": {
"original": {
"filename": "original-1qeh.jpg",
"height": 420,
"size": 28057,
"url": "/image/2014/6s/1c5cnx/original-1qeh.jpg",
"width": 300
},
"thumbnail": {
"filename": "thumbnail-1qef.jpg",
"height": 90,
"size": 3566,
"url": "/image/2014/6s/1c5cnx/thumbnail-1qef.jpg",
"width": 120
}
}
}
],
"status": "ok"
}
Самый распространённый способ загрузки файлов на сервер – использование форм. В таком случае запрос представляется следующим образом:
POST /files HTTP/1.1
Content-Length: 21929
Content-Type: multipart/form-data; boundary=----5XhQf4IXV9Q26uHM
------5XhQf4IXV9Q26uHM
Content-Disposition: form-data; name="files[]"; filename="pic.jpg"
Content-Type: image/jpeg
...bytes...
В заголовке Content-Type
передаётся значение boundary
, которое служит для разделения значений в теле запроса. Таким образом за один запрос можно передать несколько файлов. jQuery File Upload имеет соответствующую опцию [10], для множественной отправки файлов.
Современный подход позволяет отправлять бинарные данные, используя на клиенской стороне запрос типа XHR [11]. Запрос, который увидит сервер, выглядит следующим образом:
POST /files HTTP/1.1
Content-Length: 21744
Content-Disposition: attachment; filename="pic.jpg"
...bytes...
Таким способом можно передать за один запрос только один файл, имя которого будет доступно в заголовке Content-Disposition
.
Для загрузки больших файлов на клиентской стороне формируется пачка запросов с частями исходного файла. Пример запроса:
POST /files HTTP/1.1
Content-Length: 10240
Content-Range: bytes 0-10239/36431
Content-Disposition: attachment; filename="pic.jpg"
Cookie:pavo=377cb76c-2538-40d3-a3d0-13d86d206ba7
...bytes...
Имя исходного файла и значение cookie по ключу pavo используется для идентификации промежуточного файла с загруженными частями оригинала. Заголовок Content-Range
содержит информацию о том, какую часть файла предаёт клиент и каков размер оригинального файла. Если загружается последний кусок, то сервер завершает процедуру загрузки и формирует ответ с данными о полученном файле и его версиях.
Приложение написано на языке Golang. В качестве веб-фреймворка используется Gin [12]. Код разделен на два пакета(upload и attachment) и основое приложение (исполняемый файл). Пакет upload отвечает за загрузку исходных файлов или куска файла. Пакет attachment отвечает за создание конечной директории для хранения файла и его версий, конвертацию изображений, формирование данных. Основное приложение запускает веб-сервер и реализует роль контроллера.
Исходный код с небольшими примера в тестах доступен на github [1].
Приложение имеет опции запуска --host
и --storage
. Указывают host:port
для запуска веб сервера и корневую директорию хранилища соответственно.
Все запросы на загрузку приложение принимает на адрес /files
. В query_string параметре converts
можно передать параметры конвертации для изображений. Например:
POST /files?converts={"pic":"400x300"}
Для всех файлов устанавливается версия по умолчанию original. Для изображений добавляется thumbnail со значением 120x90
.
Для работы в production окружении желательно использовать веб сервер nginx [13]. В задачи веб сервера будет входить приём запросов от клиентов, запись тела во временный файл, авторизация запроса на основном приложении, отправка заголовков исходного запроса на приложение pavo.
Рекомендуемая конфигурация nginx:
server {
listen 80;
server_name pavo.local;
access_log /usr/local/var/log/nginx/pavo/access.log;
error_log /usr/local/var/log/nginx/pavo/error.log notice;
location /auth {
internal;
proxy_pass http://localhost:3000/auth/url/in/your/app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_body off;
proxy_set_header Content-Length 0;
client_max_body_size 0;
}
location /files {
auth_request /auth;
client_body_temp_path /tmp;
client_body_in_file_only on;
client_body_buffer_size 521K;
client_max_body_size 10G;
proxy_pass_request_headers on;
proxy_set_header X-FILE $request_body_file;
proxy_pass http://127.0.0.1:9073;
}
location / {
root /Path/To/Root/Of/Storage;
}
}
От клиента на веб сервер приходит запрос. Nginx дожидается получения всего запроса (эта его особенность не позволяет реализовать полноценный progress bar с проксированием на сервер приложения). После получения запроса тело будет записано во временный файл в директорию, определенную опцией client_body_temp_path
.
Перед отправкой запроса на сервер приложения pavo будет произведена авторизация. Для этого используется модуль ngx_http_auth_request_module [14]. Будет сделан подзапрос на location /auth
, который в свою очередь проксирует заголовки исходного запроса на сервер основного приложения. В случае успешной авторизации сервер должен вернуть пустое тело с кодом статуса ответа 200
.
Далее в заголовки исходного запроса добавляется новая пара, ключ X-File
, а значение – путь до временного файла с телом запроса. И только после этого получившийся запрос (заголовки и пустое тело) отправляется на прилоежение pavo. Оно обрабатывает запрос, сохраняя файлы, и возвращает ответ с данными о загруженных файлах в JSON формате.
Сервис задумывался как самостоятельное приложение в инфраструктуре веб проекта, которое берёт на себя роль загрузки и раздачи файлов, конвертации изображений, видео и аудио. С интерфейсом через HTTP Json API.
Автор: Kavkaz
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/nginx/68788
Ссылки в тексте:
[1] pavo: https://github.com/kavkaz/pavo
[2] jQuery File Uploader: https://github.com/blueimp/jQuery-File-Upload
[3] официальном сайте: https://golang.org/doc/install
[4] настроить: http://golang.org/doc/code.html#GOPATH
[5] Git: http://git-scm.com
[6] Mercurial: http://mercurial.selenic.com
[7] Subversion: https://subversion.apache.org
[8] Bazaar: http://bazaar.canonical.com/en/
[9] ImageMagick: http://www.imagemagick.org
[10] опцию: https://github.com/blueimp/jQuery-File-Upload/wiki/Options#singlefileuploads
[11] XHR: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
[12] Gin: https://github.com/gin-gonic/gin
[13] nginx: http://nginx.org
[14] ngx_http_auth_request_module: http://nginx.org/en/docs/http/ngx_http_auth_request_module.html
[15] Источник: http://habrahabr.ru/post/234693/
Нажмите здесь для печати.