一、实现方式
1、前端:前端使用jquery+webuploader来实现图片上传和预览
使用webuploader来实现模拟表单上传,将上传请求发给服务端,待服务端上传处理完成后将处理结果显示在页面上
2、服务端:使用openresty的lua-resty-upload模块来实现文件上传
接受前端的上传请求,将处理好的图片保存起来,将图片地址返回给前端
二、项目目录结构
- 2016为图片上传目录
- conf为nginx.conf配置文件目录
- html为前端文件目录(包括js和css等)
- logs为日志目录
- upload.lua为处理上传的lua代码
三、实现细节:
1、前端
前端使用jquery+webuploader来实现图片上传和预览
(1)、html代码:
要加载jquery库文件和webuploader文件
<div id="wrapper">
<div class="page-body">
<div id="post-container" class="container">
<div class="page-container">
<h1 id="demo">Demo</h1>
<p>您可以尝试文件拖拽,使用QQ截屏工具,然后激活窗口后粘贴,或者点击添加图片按钮,来体验此demo.</p>
<div id="uploader" class="wu-example">
<div class="queueList">
<div id="dndArea" class="placeholder">
<div id="filePicker"><!--按钮位置--></div>
<p>或将照片拖到这里,单次最多可选300张</p>
</div>
</div>
</div>
<div id="fileList">
<!--图片展示位置-->
</div>
</div>
</div>
</div>
</div>
(2)、js代码
// 初始化Web Uploader
var uploader = WebUploader.create({
// 选完文件后,是否自动上传。
auto: true,
// swf文件路径
swf: '/webuplader/images/Uploader.swf',
// 文件接收服务端。
server: '/upload',
// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: {
id: '#filePicker',
label: '点击选择图片'
},
dnd: '#uploader .queueList',
paste: document.body,
// 只允许选择图片文件。
accept: {
title: 'Images',
extensions: 'gif,jpg,jpeg,bmp,png',
mimeTypes: 'image/*'
}
});
uploader.on( 'uploadAccept', function( file, response ) {
console.log(response)
if (typeof response != 'undefined' && typeof response.code != 'undefined') {
if (response.code == 200) {
$('#fileList').append('<img src="http://openfile.shixinke.com/images/posts/2016/05/span>+response.imgurl+'">');
} else {
alert(response.msg)
}
} else {
console.log(response);
}
});
uploader.on( 'uploadError', function( file ) {
$( '#'+file.id ).find('p.state').text('上传出错');
});
uploader.on( 'uploadComplete', function( file ) {
$( '#'+file.id ).find('.progress').fadeOut();
});
2、nginx配置
lua_package_path '/data/www/openrestyproject/upload/lib/?.lua;/data/www/openrestyproject/upload/?.lua;;';
lua_code_cache off;
server {
server_name localhost;
listen 8000;
charset utf-8;
set $UPLOAD_ROOT /data/www/openrestyproject/upload;
error_log /data/www/openrestyproject/upload/logs/error.log;
access_log /data/www/openrestyproject/upload/logs/access.log main;
location /upload {
default_type text/html;
content_by_lua_file $UPLOAD_ROOT/upload.lua;
}
}
3、服务端
使用openresty的lua-resty-upload模块来实现文件上传
local upload = require "resty.upload"
local cjson = require "cjson"
local chunk_size = 4096
local form = upload:new(chunk_size)
local conf = {max_size=1000000, allow_exts={'jpg', 'png', 'gif'}}
local file
local file_name
--获取文件扩展名
function get_ext(res)
local ext = 'jpg'
if res == 'image/png' then
ext = 'png'
elseif res == 'image/jpg' or res == 'image/jpeg' then
ext = 'jpg'
elseif res == 'image/gif' then
ext = 'gif'
end
return ext
end
--判断某个值是否在数组中
function in_array(v, tab)
local i = false
for _, val in ipairs(tab) do
if val == v then
i = true
break
end
end
return i
end
while true do
local typ, res, err = form:read()
if typ == "header" then
if res[1] ~= "Content-Disposition" then
local file_id = ngx.md5('upload'..os.time())
local extension = get_ext(res[2])
if not extension then
ngx.say(cjson.encode({code=501, msg='未获取文件后缀', data=res}))
return
end
if not in_array(extension, conf.allow_exts) then
ngx.say(cjson.encode({code=501, msg='不支持这种文件格式', data=res}))
return
end
-- 注:这个地方请将/data/www/openrestyproject/upload/换成你的项目目录
local dir = '/data/www/openrestyproject/uploadhttp://openfile.shixinke.com/images/posts/2016/05/span>..os.date('%Y')..'http://openfile.shixinke.com/images/posts/2016/05/span>..os.date('%m')..'http://openfile.shixinke.com/images/posts/2016/05/span>..os.date('%d')..'http://openfile.shixinke.com/images/posts/2016/05/span>
local status = os.execute('mkdir -p '..dir)
if status ~= 0 then
ngx.say(cjson.encode({code=501, msg='创建目录失败'}))
return
end
file_name = dir..file_id.."."..extension
if file_name then
file = io.open(file_name, "w+")
if not file then
ngx.say(cjson.encode({code=500, msg='failed to open file',imgurl=''}))
return
end
end
end
elseif typ == "body" then
if type(tonumber(res)) == 'number' and tonumber(res) > conf.max_size then
ngx.say(cjson.encode({code=501, msg='文件超过规定大小', data=res}))
return
end
if file then
file:write(res)
end
elseif typ == "part_end" then
if file then
file:close()
file = nil
end
elseif typ == "eof" then
file_name = string.gsub(file_name, '/data/www/openrestyproject/uploadhttp://openfile.shixinke.com/images/posts/2016/05/span>, '')
-- 注:这个地方请将/data/www/openrestyproject/upload/换成你的项目目录
ngx.say(cjson.encode({code=200, msg='上传成功!',imgurl= file_name}))
break
else
end
end
四、项目效果
项目源码下载地址:https://github.com/shixinke/openresty-practices/tree/master/upload
1、上传前:
2、上传后: