参考:使用openresty的lua-resty-upload实现文件上传
- 前几天在群里看到苏苏使用到了图床,以链接的形式发送图片,这样发到群里的图片就不担心失效了。
- 看到谢尔在群里发自我介绍,后面还放了一个飞书链接,里面有更详细的介绍。我在想把飞书链接换成图片链接是不是也挺好的?
图床,图床,图床...对了!Nginx!!!
突然想到了Nginx,我是不是可以做点什么,正好当练习了?
于是就打算用OpenResty搭个简易的图床,后来因为懒得判断文件类型等原因,就觉得不如实现一个网盘吧,先简单实现上传和下载功能就好。
配置
配置文件如下:
events{ # events块是必须要有的,不然会报错
}
http {
server {
listen 8081;
root html;
charset 'utf-8';
autoindex on; # 启用目录浏览功能
autoindex_exact_size off; # 显示文件大概的大小,并使用人类易读的单位
location /upfile {
client_max_body_size 2G; # 限制上传文件的大小,默认为1M
content_by_lua_file lua/upload.lua;
}
# 当访问files文件夹中的文件时,修改header头
# 这样在浏览器中点击文件就会进行下载,而不是打开
location ~ ^/files/(.+)$ {
add_header Content-Disposition attachment;
add_header Content-Type application/octet-stream;
}
}
}
前端界面及代码
简单实现了上传界面,如图:
前端界面1.png代码如下:
<!--index.html-->
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form id="upload-form" action="upfile" method="post" enctype="multipart/form-data" >
<input type="file" id="upload" name="upload" /> <br />
<input type="submit" value="开始上传" />
</form>
</body>
</html>
文件浏览界面,点击文件可下载,如图:
前端界面1.png
后端代码
-- upload.lua
--==========================================
-- 文件上传
--==========================================
local upload = require "resty.upload"
local cjson = require "cjson"
local chunk_size = 4096
local form, err = upload:new(chunk_size)
if not form then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
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 function get_file_name(header_value)
local kvlist = string.split(header_value, ';')
for _, kv in ipairs(kvlist) do
local seg = string.trim(kv)
if seg:find("filename") then
local kvfile = string.split(seg, "=")
local filename = string.sub(kvfile[2], 2, -2)
return filename
end
end
end
-- 文件保存的根路径
local saveRootPath = "./html/files/";
-- 保存的文件对象
local fileToSave
--文件是否成功保存
local ret_save = false
while true do
local typ, res, err = form:read()
if not typ then
ngx.say("failed to read: ", err)
return
end
if typ == "header" then
-- 开始读取header
local key = res[1]
local value = res[2]
if key == "Content-Disposition" then
-- 解析出本次上传的文件名
-- form-data; name="testFileName"; filename="testfile.txt"
local filename = get_file_name(value)
if filename then
local fullpath = saveRootPath .. filename
if io.open(fullpath) then
ngx.say("文件已存在")
break
end
fileToSave = io.open(fullpath, "w+")
if not fileToSave then
ngx.say("文件打开失败 ", filename)
break
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
ngx.say("上传文件成功。")
else
ngx.say("上传文件失败。")
end
网友评论