upload: Lector y analizador de streaming para la carga de archivos http basado en nginx-module-lua cosocket
Instalación
Si no has configurado la suscripción al repositorio RPM, regístrate. Luego, puedes proceder con los siguientes pasos.
CentOS/RHEL 7 o 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
Para usar esta biblioteca Lua con NGINX, asegúrate de que nginx-module-lua esté instalado.
Este documento describe lua-resty-upload v0.11 lanzado el 19 de enero de 2023.
Esta biblioteca Lua es una API de carga de archivos por streaming para el módulo ngx_lua de nginx:
http://wiki.nginx.org/HttpLuaModule
El tipo MIME multipart/form-data es compatible.
La API de esta biblioteca simplemente devuelve tokens uno por uno. El usuario solo necesita llamar al método read repetidamente hasta que se devuelva un tipo de token nil. Para cada token devuelto del método read, solo verifica el primer valor de retorno para el tipo de token actual. El tipo de token puede ser header, body y part end. Cada campo de formulario multipart/form-data analizado consiste en varios tokens header que contienen cada encabezado de campo, varios tokens body que contienen cada fragmento de datos del cuerpo y una bandera part end que indica el final del campo.
Así es como funciona la lectura por streaming. Incluso para gigabytes de datos de archivo de entrada, la memoria utilizada en el espacio Lua puede ser pequeña y constante, siempre que el usuario no acumule los fragmentos de datos de entrada por sí misma.
Esta biblioteca Lua aprovecha la API de cosocket de ngx_lua, que garantiza un comportamiento 100% no bloqueante.
Ten en cuenta que se requiere al menos ngx_lua 0.7.9 o OpenResty 1.2.4.14.
Sinopsis
server {
location /test {
content_by_lua '
local upload = require "resty.upload"
local cjson = require "cjson"
local chunk_size = 5 -- debe establecerse en 4096 o 8192
-- para configuraciones del mundo real
local form, err = upload:new(chunk_size)
if not form then
ngx.log(ngx.ERR, "falló al crear la carga: ", err)
ngx.exit(500)
end
form:set_timeout(1000) -- 1 seg
while true do
local typ, res, err = form:read()
if not typ then
ngx.say("falló al leer: ", err)
return
end
ngx.say("leído: ", cjson.encode({typ, res}))
if typ == "eof" then
break
end
end
local typ, res, err = form:read()
ngx.say("leído: ", cjson.encode({typ, res}))
';
}
}
Una salida típica de la ubicación /test definida arriba es:
leído: ["header",["Content-Disposition","form-data; name=\"file1\"; filename=\"a.txt\"","Content-Disposition: form-data; name=\"file1\"; filename=\"a.txt\""]]
leído: ["header",["Content-Type","text\/plain","Content-Type: text\/plain"]]
leído: ["body","Hello"]
leído: ["body",", wor"]
leído: ["body","ld"]
leído: ["part_end"]
leído: ["header",["Content-Disposition","form-data; name=\"test\"","Content-Disposition: form-data; name=\"test\""]]
leído: ["body","value"]
leído: ["body","\r\n"]
leído: ["part_end"]
leído: ["eof"]
leído: ["eof"]
Puedes usar la biblioteca lua-resty-string para calcular el resumen SHA-1 y MD5 de los datos del archivo de manera incremental. Aquí hay un ejemplo:
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("falló al leer: ", 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("falló al abrir el archivo ", 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
-- no hacer nada
end
end
Si deseas calcular sumas MD5 para los archivos subidos, solo usa el módulo resty.md5 proporcionado por la biblioteca lua-resty-string. Tiene una API similar a resty.sha1.
Para la carga de archivos grandes, es importante no almacenar todos los datos en memoria. Es decir, nunca debes acumular fragmentos de datos ni en una gran cadena Lua ni en una gran tabla Lua. Debes escribir el fragmento de datos en archivos lo antes posible y desechar el fragmento de datos inmediatamente (para permitir que el GC de Lua lo libere).
En lugar de escribir el fragmento de datos en archivos (como se muestra en el ejemplo anterior), también puedes escribir los fragmentos de datos en conexiones de cosocket ascendentes si no deseas guardar los datos en sistemas de archivos locales.
Uso
local upload = require "resty.upload"
local form, err = upload:new(self, chunk_size, max_line_size, preserve_body)
chunk_size tiene un valor predeterminado de 4096. Es el tamaño utilizado para leer datos del socket.
max_line_size tiene un valor predeterminado de 512. Es el límite de tamaño para leer el encabezado del cuerpo fragmentado.
Por defecto, lua-resty-upload consumirá el cuerpo de la solicitud. Para el modo proxy, esto significa que el upstream no verá el cuerpo. Cuando preserve_body se establece en true, el cuerpo de la solicitud se conservará. Ten en cuenta que esta opción no es gratuita. Cuando se habilita, duplicará el uso de memoria de resty.upload.
Véase también
- el módulo ngx_lua
- la biblioteca lua-resty-string
- la biblioteca lua-resty-memcached
- la biblioteca lua-resty-redis
- la biblioteca lua-resty-mysql
GitHub
Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-upload.