前言
前几天使用nginx
+nginx_upload_module
+python(回调处理程序)
搭建了一个简单的文件服务器,网上很多人都建议使用Lua
去扩展nginx
的功能,所以琢磨了下如何使用Lua
语言去对nginx
去做功能扩展,网上查阅了许多资料,发现环境搭建还是比较麻烦的,需要安装LuaJIT
,nginx
的还需要一起编译ngx_devel_kit
、lua-nginx-module
模块,然后发现了openresty
。
之前在了解kong
网关的时候知道过openresty
,但是没有去了解研究过,这次的机会恰好让我了解学习了一下。
关于openresty
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。
OpenResty® 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
-- 摘自官网
开始搭建
安装OpenResty
关于OpenResty的安装方法官方文档有非常详细的说明,而且非常全面,可能和章亦春
的性格有关吧,非常简单,快速。
-
直接使用安装工具安装(
推荐
)- 官方教程
- centos 示例
-
# add the yum repo: wget https://openresty.org/package/centos/openresty.repo sudo mv openresty.repo /etc/yum.repos.d/ # update the yum index: sudo yum check-update sudo yum install -y openresty
我是使用yum安装的,默认的安装位置在
/usr/local/openresty
-
[root@cloudfile openresty]# pwd /usr/local/openresty [root@cloudfile openresty]# ls bin conf COPYRIGHT luajit lualib nginx openssl pcre site zlib
编写文件处理的Lua脚本
-
文件位置:
/usr/local/openresty/nginx/conf/lua/update.lua
-
-- upload.lua --========================================== -- 文件上传 --========================================== -- 导入模块 local upload = require "resty.upload" local cjson = require "cjson" -- 定义回复的结构 -- 基本结构 local function response(status,msg,data) local res = {} res["status"] = status res["msg"] = msg res["data"] = data local jsonData = cjson.encode(res) return jsonData end -- 默认成功的response local function success() return response(0,"success",nil) end -- 默认带数据的response local function successWithData(data) return response(0,"success",data) end -- 失败的response local function failed( msg ) return response(-1,msg,nil) end -- end local chunk_size = 4096 -- 获取请求的form local form, err = upload:new(chunk_size) if not form then ngx.log(ngx.ERR, "failed to new upload: ", err) ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) ngx.say(failed("没有获取到文件")) end form:set_timeout(1000) -- 定义 字符串 split 分割 属性 string.split = function(s, p) local rt= {} string.gsub(s, '[^'..p..']+', function(w) table.insert(rt, w) end ) return rt end -- 定义 支持字符串前后 trim 属性 string.trim = function(s) return (s:gsub("^%s*(.-)%s*$", "%1")) end -- 文件保存的根路径 local saveRootPath = ngx.var.store_dir -- 保存的文件对象 local fileToSave -- 标识文件是否成功保存 local ret_save = false -- 实际收到的文件名 local rawFileName -- 实际保存的文件名 local filename -- 开始处理数据 while true do -- 读取数据 local typ, res, err = form:read() if not typ then ngx.say(failed(err)) return end -- 开始读取 http header if typ == "header" then -- 解析出本次上传的文件名 local key = res[1] local value = res[2] if key == "Content-Disposition" then -- 解析出本次上传的文件名 -- form-data; name="keyName"; filename="xxx.xx" local kvlist = string.split(value, ';') for _, kv in ipairs(kvlist) do local seg = string.trim(kv) if seg:find("filename") then local kvfile = string.split(seg, "=") -- 实际的文件名 rawFileName = string.sub(kvfile[2], 2, -2) -- 重命文件名 filename = os.time().."-"..rawFileName if filename then -- 打开(创建)文件 fileToSave = io.open(saveRootPath .."/" .. filename, "w+") if not fileToSave then -- ngx.say("failed to open file ", filename) ngx.say(failed("打开文件失败")) return end break end end end end elseif typ == "body" then -- 开始读取 http body if fileToSave then -- 写入文件内容 fileToSave:write(res) end elseif typ == "part_end" then -- 文件写结束,关闭文件 if fileToSave then fileToSave:close() fileToSave = nil end ret_save = true -- 文件读取结束 elseif typ == "eof" then break else ngx.log(ngx.INFO, "do other things") end end if ret_save then local uploadData = {} uploadData["file"] = rawFileName uploadData["url"] = "http://xxx.com/download/"..filename ngx.say(successWithData(uploadData)) else ngx.say(failed("系统异常")) end
编写nginx配置文件
文件位置:/usr/local/openresty/nginx/conf/nginx.conf
user root;
worker_processes 20;
error_log logs/error.log notice;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
server {
listen 8080;
server_name localhost;
server_tokens off;
# 最大允许上传的文件大小
client_max_body_size 300m;
set $store_dir "/var/www/download"; # 文件存储路径
# 文件上传接口:http://xxx.com/uploadfile
add_header Strict-Transport-Security 'max-age=15552000';
add_header X-Content-Type-Options 'nosniff';
add_header X-Robots-Tag 'none';
add_header X-Download-Options 'noopen';
add_header X-Permitted-Cross-Domain-Policies 'none';
add_header X-XSS-Protection '1;mode=block';
add_header X-Frame-Options SAMEORIGIN;
location /uploadfile {
# 实现文件上传的逻辑
content_by_lua_file conf/lua/update.lua;
}
# 文件下载入口: http://xxx.com/download
location /download {
alias /var/www/download;
autoindex on;
autoindex_localtime on;
}
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
mkdir -p /var/www/download
启动测试
这里启动nginx的方式和普通的nginx是一样的:
- 启动:
/usr/local/openresty/nginx/sbin/nginx
- 关闭:
/usr/local/openresty/nginx/sbin/nginx -s stop
- 重新加载配置:
/usr/local/openresty/nginx/sbin/nginx -s reload
上传文件
POST /uploadfile
下载文件
访问 GET /download
即可