使用OpenResty搭建简单的文件服务器
前言
前几天使用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 80;
server_name localhost;
# 最大允许上传的文件大小
client_max_body_size 300m;
set $store_dir "/var/www/download"; # 文件存储路径
# 文件上传接口:http://xxx.com/uploadfile
location /uploadfile {
# 实现文件上传的逻辑
content_by_lua_file conf/lua/upload.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;
}
}
}
启动测试
这里启动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
即可