使用OpenResty搭建简单的文件服务器

前言

前几天使用nginx+nginx_upload_module+python(回调处理程序)搭建了一个简单的文件服务器,网上很多人都建议使用Lua去扩展nginx的功能,所以琢磨了下如何使用Lua语言去对nginx去做功能扩展,网上查阅了许多资料,发现环境搭建还是比较麻烦的,需要安装LuaJITnginx的还需要一起编译ngx_devel_kitlua-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的安装方法官方文档有非常详细的说明,而且非常全面,可能和章亦春的性格有关吧,非常简单,快速。

  • 直接使用安装工具安装(推荐)

    # 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即可

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355