用openresty和lua实现壁纸投票功能
- 创业
- 2025-08-23 19:21:02

背景
之前做了一个随机壁纸接口,但是不知道大家喜欢对壁纸的喜好,所以干脆在实现一个投票功能,让用户给自己喜欢的壁纸进行投票。
原理说明1.当访问http://demo /vote/时,会从/home/jobs/webs/imgs及子目录下获取图片列表,然后生成一个投票的vote.html页面,并自动跳转到http://demo /vote/vote.html,点击图片即可选中/取消选中,在右下角始终有个悬浮按钮"投票"
2.当点击"投票"之后,会POST调用http://demo /vote/,把结果记录到home/data/vote_stats.json,里面记录了得票的图片路径和票数。
3.之后会生成一个result.html页面,并自动跳转到http://demo /vote/result.html,壁纸根据得票数自动排序,最下方有个"返回投票页"的按钮
实战创建vote目录,存放vote.html和result.html
mkdir /home/jobs/webs/vote chmod 755 /home/jobs/webs/vote -R chown nginx:nginx /home/jobs/webs/vote -R编写lua脚本 cat /etc/nginx/conf.d/vote.lua
package.path = package.path .. ";/usr/local/share/lua/5.1/?.lua;/usr/share/lua/5.1/?.lua" package.cpath = package.cpath .. ";/usr/local/lib/lua/5.1/?.so;/usr/lib64/lua/5.1/?.so" local cjson = require "cjson" local lfs = require "lfs" -- 获取图片列表 local function get_images(path) local images = {} for file in lfs.dir(path) do if file ~= "." and file ~= ".." then local full_path = path .. "/" .. file local attr = lfs.attributes(full_path) if attr.mode == "file" and (file:match("%.jpg$") or file:match("%.png$")) then table.insert(images, file) elseif attr.mode == "directory" then local sub_images = get_images(full_path) for _, sub_image in ipairs(sub_images) do table.insert(images, file .. "/" .. sub_image) end end end end return images end -- 读取统计结果 local function read_stats() local stats_path = "/home/data/vote_stats.json" local stats = {} local file = io.open(stats_path, "r") if file then local content = file:read("*a") file:close() stats = cjson.decode(content) or {} end return stats end -- 保存统计结果 local function save_stats(stats) local stats_path = "/home/data/vote_stats.json" local file = io.open(stats_path, "w") if file then file:write(cjson.encode(stats)) file:close() else ngx.log(ngx.ERR, "Failed to open file: ", stats_path) end end -- 生成投票页面 HTML local function generate_vote_html() local images = get_images("/home/jobs/webs/imgs") -- 生成 HTML 内容 local html = [[ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>壁纸投票</title> <style> body { font-family: Arial, sans-serif; } img { width: 200px; margin: 10px; border: 3px solid transparent; cursor: pointer; } img.selected { border: 3px solid #007bff; } .image-container { display: flex; flex-wrap: wrap; } .image-item { margin: 10px; text-align: center; } .floating-button { position: fixed; bottom: 20px; right: 20px; padding: 10px 20px; background-color: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; } .floating-button:hover { background-color: #0056b3; } </style> <script> function toggleSelection(img) { img.classList.toggle("selected"); var checkbox = img.parentElement.querySelector('input[type="checkbox"]'); checkbox.checked = !checkbox.checked; } </script> </head> <body> <h1>壁纸投票</h1> <form method="post" action="/vote/"> <div class="image-container"> ]] -- 添加图片和复选框 for _, img in ipairs(images) do html = html .. string.format([[<div class="image-item"> <input type="checkbox" name="%s" id="%s" style="display: none;"> <label for="%s"><img src="/vote/imgs/%s" alt="%s" onclick="toggleSelection(this)"></label> </div>]], img, img, img, img, img) end html = html .. [[ </div> <button type="submit" class="floating-button">提交投票</button> </form> </body> </html> ]] -- 将 HTML 内容写入文件 local html_file_path = "/home/jobs/webs/vote/vote.html" local file = io.open(html_file_path, "w") if file then file:write(html) file:close() else ngx.log(ngx.ERR, "Failed to open file: ", html_file_path) end end -- 生成投票结果页面 HTML local function generate_result_html() local stats = read_stats() -- 按票数排序 local sorted_stats = {} for img, data in pairs(stats) do table.insert(sorted_stats, {img = img, count = data.count}) end table.sort(sorted_stats, function(a, b) return a.count > b.count end) -- 生成 HTML 内容 local html = [[ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>投票结果</title> <style> body { font-family: Arial, sans-serif; } .stats { margin-top: 20px; } img { width: 200px; margin: 10px; border: 1px solid #ccc; } .image-container { display: flex; flex-wrap: wrap; } .image-item { margin: 10px; text-align: center; } </style> </head> <body> <h1>投票结果</h1> <div class="image-container"> ]] -- 显示投票结果 for _, data in ipairs(sorted_stats) do if data.count > 0 then html = html .. string.format([[<div class="image-item"> <img src="/vote/imgs/%s" alt="%s"> <p>%s: %d 票</p> </div>]], data.img, data.img, data.img, data.count) end end html = html .. [[ </div> <a href="/vote/vote.html">返回投票页面</a> </body> </html> ]] -- 将 HTML 内容写入文件 local result_file_path = "/home/jobs/webs/vote/result.html" local file = io.open(result_file_path, "w") if file then file:write(html) file:close() else ngx.log(ngx.ERR, "Failed to open file: ", result_file_path) end end -- 处理投票 local function handle_vote() -- 确保请求体已读取 ngx.req.read_body() -- 获取 POST 参数 local args, err = ngx.req.get_post_args() if not args then ngx.log(ngx.ERR, "Failed to get POST args: ", err) ngx.exit(ngx.HTTP_BAD_REQUEST) end -- 获取投票人 IP local voter_ip = ngx.var.remote_addr -- 读取统计结果 local stats = read_stats() -- 更新投票数据 for img, _ in pairs(args) do if not stats[img] then stats[img] = {count = 0, voters = {}} end stats[img].count = stats[img].count + 1 end -- 保存统计结果 save_stats(stats) -- 生成 HTML 文件 generate_vote_html() -- 更新投票页面 generate_result_html() -- 生成投票结果页面 -- 重定向到投票结果页面 ngx.redirect("/vote/result.html") end -- 处理请求 if ngx.var.request_method == "POST" then handle_vote() else generate_vote_html() -- 生成投票页面 ngx.redirect("/vote/vote.html") -- 重定向到投票页面 end对应的openresty配置
location /vote/vote.html { try_files /vote/vote.html =404; } location /vote/result.html { try_files /vote/result.html =404; } location /vote/ { lua_need_request_body on; # 启用请求体读取 content_by_lua_file /etc/nginx/conf.d/vote.lua; } # 静态图片服务,用于展示壁纸 location /vote/imgs/ { alias /home/jobs/webs/imgs/; } 效果用openresty和lua实现壁纸投票功能由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“用openresty和lua实现壁纸投票功能”