upload: Потоковый читатель и парсер для http загрузки файлов на основе nginx-module-lua cosocket
Установка
Если вы еще не настроили подписку на репозиторий RPM, зарегистрируйтесь. Затем вы можете продолжить с следующими шагами.
CentOS/RHEL 7 или Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-upload
CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-upload
Чтобы использовать эту библиотеку Lua с NGINX, убедитесь, что установлен nginx-module-lua.
Этот документ описывает lua-resty-upload v0.11, выпущенную 19 января 2023 года.
Эта библиотека Lua является API для потоковой загрузки файлов для модуля ngx_lua nginx:
http://wiki.nginx.org/HttpLuaModule
Поддерживается MIME-тип multipart/form-data.
API этой библиотеки просто возвращает токены один за другим. Пользователю нужно просто повторно вызывать метод read, пока не будет возвращен токен типа nil. Для каждого токена, возвращенного из метода read, просто проверьте первое возвращаемое значение для текущего типа токена. Тип токена может быть header, body и part end. Каждое поле формы multipart/form-data, которое разбирается, состоит из нескольких токенов header, содержащих заголовок каждого поля, нескольких токенов body, содержащих каждую часть данных тела, и флага part end, указывающего на конец поля.
Вот как работает потоковое чтение. Даже для гигабайтов входных данных файла, память, используемая в Lua, может быть небольшой и постоянной, если пользователь не накапливает части входных данных самостоятельно.
Эта библиотека Lua использует API cosocket ngx_lua, который обеспечивает 100% неблокирующее поведение.
Обратите внимание, что требуется как минимум ngx_lua 0.7.9 или OpenResty 1.2.4.14.
Синопсис
server {
location /test {
content_by_lua '
local upload = require "resty.upload"
local cjson = require "cjson"
local chunk_size = 5 -- должен быть установлен на 4096 или 8192
-- для реальных условий
local form, err = upload:new(chunk_size)
if not form then
ngx.log(ngx.ERR, "не удалось создать загрузку: ", err)
ngx.exit(500)
end
form:set_timeout(1000) -- 1 секунда
while true do
local typ, res, err = form:read()
if not typ then
ngx.say("не удалось прочитать: ", err)
return
end
ngx.say("прочитано: ", cjson.encode({typ, res}))
if typ == "eof" then
break
end
end
local typ, res, err = form:read()
ngx.say("прочитано: ", cjson.encode({typ, res}))
';
}
}
Типичный вывод местоположения /test, определенного выше:
прочитано: ["header",["Content-Disposition","form-data; name=\"file1\"; filename=\"a.txt\"","Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\""]]
прочитано: ["header",["Content-Type","text\/plain","Content-Type: text\/plain"]]
прочитано: ["body","Hello"]
прочитано: ["body",", wor"]
прочитано: ["body","ld"]
прочитано: ["part_end"]
прочитано: ["header",["Content-Disposition","form-data; name=\"test\"","Content-Disposition: form-data; name=\"test\""]]
прочитано: ["body","value"]
прочитано: ["body","\r\n"]
прочитано: ["part_end"]
прочитано: ["eof"]
прочитано: ["eof"]
Вы можете использовать библиотеку lua-resty-string для вычисления SHA-1 и MD5 дайджеста данных файла по частям. Вот такой пример:
local resty_sha1 = require "resty.sha1"
local upload = require "resty.upload"
local chunk_size = 4096
local form = upload:new(chunk_size)
local sha1 = resty_sha1:new()
local file
while true do
local typ, res, err = form:read()
if not typ then
ngx.say("не удалось прочитать: ", err)
return
end
if typ == "header" then
local file_name = my_get_file_name(res)
if file_name then
file = io.open(file_name, "w+")
if not file then
ngx.say("не удалось открыть файл ", file_name)
return
end
end
elseif typ == "body" then
if file then
file:write(res)
sha1:update(res)
end
elseif typ == "part_end" then
file:close()
file = nil
local sha1_sum = sha1:final()
sha1:reset()
my_save_sha1_sum(sha1_sum)
elseif typ == "eof" then
break
else
-- ничего не делать
end
end
Если вы хотите вычислить MD5 суммы для загруженных файлов, просто используйте модуль resty.md5, поставляемый с библиотекой lua-resty-string. У него аналогичный API, как у resty.sha1.
Для загрузки больших файлов важно не буферизовать все данные в памяти. То есть, вы никогда не должны накапливать части данных либо в огромной строке Lua, либо в огромной таблице Lua. Вы должны записывать части данных в файлы как можно скорее и немедленно выбрасывать часть данных (чтобы позволить сборщику мусора Lua освободить их).
Вместо записи части данных в файлы (как показано в примере выше), вы также можете записывать части данных в соединения cosocket upstream, если не хотите сохранять данные на локальных файловых системах.
Использование
local upload = require "resty.upload"
local form, err = upload:new(self, chunk_size, max_line_size, preserve_body)
chunk_size по умолчанию равен 4096. Это размер, используемый для чтения данных из сокета.
max_line_size по умолчанию равен 512. Это предельный размер для чтения заголовка частичного тела.
По умолчанию lua-resty-upload будет потреблять тело запроса. Для режима прокси это означает, что upstream не увидит тело. Когда preserve_body установлен в true, тело запроса будет сохранено. Обратите внимание, что эта опция не бесплатна. При включении она удваивает использование памяти resty.upload.
См. также
- модуль ngx_lua
- библиотека lua-resty-string
- библиотека lua-resty-memcached
- библиотека lua-resty-redis
- библиотека lua-resty-mysql
GitHub
Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-upload.