用openresty和lua实现壁纸投票功能
背景
之前做了一个随机壁纸接口,但是不知道大家喜欢对壁纸的喜好,所以干脆在实现一个投票功能,让用户给自己喜欢的壁纸进行投票。
原理说明
1.当访问http://demo.com/vote/时,会从/home/jobs/webs/imgs及子目录下获取图片列表,然后生成一个投票的vote.html页面,并自动跳转到http://demo.com/vote/vote.html,点击图片即可选中/取消选中,在右下角始终有个悬浮按钮"投票"
2.当点击"投票"之后,会POST调用http://demo.com/vote/,把结果记录到home/data/vote_stats.json,里面记录了得票的图片路径和票数。
3.之后会生成一个result.html页面,并自动跳转到http://demo.com/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) doif file ~= "." and file ~= ".." thenlocal full_path = path .. "/" .. filelocal attr = lfs.attributes(full_path)if attr.mode == "file" and (file:match("%.jpg$") or file:match("%.png$")) thentable.insert(images, file)elseif attr.mode == "directory" thenlocal sub_images = get_images(full_path)for _, sub_image in ipairs(sub_images) dotable.insert(images, file .. "/" .. sub_image)endendendendreturn 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 thenlocal content = file:read("*a")file:close()stats = cjson.decode(content) or {}endreturn stats
end-- 保存统计结果
local function save_stats(stats)local stats_path = "/home/data/vote_stats.json"local file = io.open(stats_path, "w")if file thenfile:write(cjson.encode(stats))file:close()elsengx.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) dohtml = 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)endhtml = 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 thenfile:write(html)file:close()elsengx.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) dotable.insert(sorted_stats, {img = img, count = data.count})endtable.sort(sorted_stats, function(a, b)return a.count > b.countend)-- 生成 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) doif data.count > 0 thenhtml = 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)endendhtml = 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 thenfile:write(html)file:close()elsengx.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 thenngx.log(ngx.ERR, "Failed to get POST args: ", err)ngx.exit(ngx.HTTP_BAD_REQUEST)end-- 获取投票人 IPlocal voter_ip = ngx.var.remote_addr-- 读取统计结果local stats = read_stats()-- 更新投票数据for img, _ in pairs(args) doif not stats[img] thenstats[img] = {count = 0, voters = {}}endstats[img].count = stats[img].count + 1end-- 保存统计结果save_stats(stats)-- 生成 HTML 文件generate_vote_html() -- 更新投票页面generate_result_html() -- 生成投票结果页面-- 重定向到投票结果页面ngx.redirect("/vote/result.html")
end-- 处理请求
if ngx.var.request_method == "POST" thenhandle_vote()
elsegenerate_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实现壁纸投票功能
背景 之前做了一个随机壁纸接口,但是不知道大家喜欢对壁纸的喜好,所以干脆在实现一个投票功能,让用户给自己喜欢的壁纸进行投票。 原理说明 1.当访问http://demo.com/vote/时,会从/home/jobs/webs/imgs及子目录下获取图片列表&…...
Vue 监听属性(watch)
Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue 监听属性(watch) 目录 监听属性 监听值改变 使用watch实现 区别 总结 监听属性 通过watch来响应数据的变化。 虽然大多数情况计算属性都可以满足需要,但有时还是需要使用侦听器。…...
mysql查看binlog日志
mysql 配置、查看binlog日志: 示例为MySQL8.0 1、 检查binlog开启状态 SHOW VARIABLES LIKE ‘log_bin’; 如果未开启,修改配置my.ini 开启日志 安装目录配置my.ini(mysql8在data目录) log-binmysql-bin(开启日志并指定日志前缀ÿ…...
BiRefNet C++ TensorRT (二分类图像分割)
BiRefNet C TensorRT (二分类图像分割) 利用TensorRT和CUDA的双边参考网络(BiRefNet)的高性能c实现,针对实时高分辨率二分类图像分割进行了优化。 BiRefNet c TENSORRT旨在有效地在GPU上运行双边参考分割任务。通过利…...
蓝桥杯篇---IAP15F2K61S2矩阵键盘
文章目录 前言简介矩阵键盘的工作原理1.行扫描2.检测列状态3.按键识别 硬件连接1.行线2.列线 矩阵键盘使用步骤1.初始化IO口2.扫描键盘3.消抖处理4.按键识别 示例代码:4x4矩阵键盘扫描示例代码:优化后的矩阵键盘扫描注意事项1.消抖处理2.扫描频率3.IO口配…...
【ARM】MDK在编译 i.MXRT1芯片的时候出现报错Error: L6079E
1、 文档目标 解决MDK在编译 i.MXRT1芯片的时候出现报错Error: L6079E 2、 问题场景 客户在使用NXP 的NXP i.MXRT1050的芯片进行工程构建的时候出现下面的报错信息: Error: L6079E: Subtool invocation error: Error executing armcc. The system could not find…...
论文笔记(七十二)Reward Centering(二)
Reward Centering(二) 文章概括摘要2 简单的奖励中心 文章概括 引用: article{naik2024reward,title{Reward Centering},author{Naik, Abhishek and Wan, Yi and Tomar, Manan and Sutton, Richard S},journal{arXiv preprint arXiv:2405.0…...
LeetCode 2595.奇偶位数:位运算
【LetMeFly】2595.奇偶位数:位运算 力扣题目链接:https://leetcode.cn/problems/number-of-even-and-odd-bits/ 给你一个 正 整数 n 。 用 even 表示在 n 的二进制形式(下标从 0 开始)中值为 1 的偶数下标的个数。 用 odd 表示…...
推荐几款较好的开源成熟框架
一. 若依: 1. 官方网站:https://doc.ruoyi.vip/ruoyi/ 2. 若依SpringBootVueElement 的后台管理系统:https://gitee.com/y_project/RuoYi-Vue 3. 若依SpringBootVueElement 的后台管理系统:https://gitee.com/y_project/RuoYi-Cl…...
基于知识图谱的问答系统:后端Python+Flask,数据库Neo4j,前端Vue3(提供源码)
基于知识图谱的问答系统:后端PythonFlask,数据库Neo4j,前端Vue3 引言 随着人工智能技术的不断发展,知识图谱作为一种结构化的知识表示方式,逐渐成为问答系统的重要组成部分。本文将介绍如何构建一个基于知识图谱的问答…...
【华为机试】HJ80 整型数组合并
解法一: HashSet>List列表 Collections.sort(list)对列表进行排序 import java.util.*; import java.util.HashSet;// 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner sc new Scanner(…...
day17-后端Web原理——SpringBoot原理
目录 SpingBoot原理1. 配置优先级2. Bean管理2.1 获取Bean2.2 Bean作用域2.3 第三方Bean 3. SpringBoot原理3.1 起步依赖3.2 自动配置3.2.1 概述3.2.2 常见方案3.2.2.1 概述3.2.2.2 方案一3.2.2.3 方案二 3.2.3 原理分析3.2.3.1 源码跟踪3.2.3.2 Conditional 4. Web后端开发总结…...
我们来学nginx -- work process
题记 工作进程能处理大量的并发请求几乎不会阻塞Worker进程可绑定到固定的核,避免CPU频繁地上下文切换看样子,还得转为人话 大白话 作为一般的应用的开发大部分人没有很强的底层技术基础如果深究,涉及复杂技术点,很容易迷惘为什…...
马拉车算法
Manacher算法 ,用于处理最长回文字符串的问题,可以在O(n)的情况下,求出一个字符串的最长回文字符串 回文串的基础解法: 以每个点为中心对称点,看左右两边的点是否相同。这种算法的时间复杂度为O࿰…...
【PLL】应用:同步
1. 用于时钟去偏移的PLL 时钟频率增加内部时钟与外部时钟的偏移,在芯片之间通信时很重要时钟偏移可能是由时钟树引起的,该时钟树缓冲外部时钟以驱动大量内部节点 芯片间通信中的时钟偏移问题 芯片1和芯片2共享外部时钟CKext芯片内部逻辑电路操作的实际时…...
golang常用库之-swaggo/swag根据注释生成接口文档
文章目录 golang常用库之-swaggo/swag库根据注释生成接口文档什么是swaggo/swag golang常用库之-swaggo/swag库根据注释生成接口文档 什么是swaggo/swag github:https://github.com/swaggo/swag 参考文档:https://golang.halfiisland.com/community/pk…...
Go入门之数组与切片
var arr1 [...]int{1, 2, 3}fmt.Println(len(arr1)) 数组长度不能扩展 var arr2 [...]int{0: 100, 5: 101}fmt.Println(len(arr2)) } 指定索引初始化 可以通过for和range遍历 值类型:基本数据类型和数组都是值类型,改变副本的值不会改变本身的值 切片为引用数…...
30天开发操作系统 第22天 -- 用C语言编写应用程序
前言 在昨天的最后我们成功干掉了crack2.hrb, 今天我们要尝试一下更厉害的攻击手段。 所以说, 从现在开始又要打开坏人模式了哟,嘿嘿嘿 虽然把操作系统的段地址存入DS这一招现在已经不能用了,不过我可不会善罢甘休的。我要想个更厉害的招数,…...
后端开发:开启技术世界的新大门
在互联网的广阔天地中,后端开发宛如一座大厦的基石,虽不直接与用户 “面对面” 交流,却默默地支撑着整个互联网产品的稳定运行。它是服务器端编程的核心领域,负责处理数据、执行业务逻辑以及与数据库和其他后端服务进行交互。在当…...
20250220解决使用top指令查看荣品PRO-RK3566开发板的CPU占用率为400%的问题
20250220解决使用top指令查看荣品PRO-RK3566开发板的CPU占用率为400%的问题 2025/2/20 19:14 缘起,使用荣品PRO-RK3566开发板配套的百度网盘中的SDK:Android13编译之后,查看RK3566的CPU占用率为400%。 开机就是400%,什么时候都是4…...
win32汇编环境,窗口程序中使用月历控件示例二
;运行效果 ;win32汇编环境,窗口程序中使用月历控件示例二 ;以下示例有2个操作,即将每周的开始日进行改变,将默认的周日开始改为周一开始,同时实现点击哪个日期,则设定为哪个日期 ;直接抄进RadAsm可编译运行。重要部分加备注。 ;下面为asm文件 ;>>>>>>>…...
java毕业设计之医院门诊挂号系统(源码+文档)
风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的医院门诊挂号系统。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 医院门诊挂号系统的主要使用者…...
今日行情明日机会——20250220
明日投资机会分析 根据提供的数据,市场热点集中在机器人、人工智能、军工、化工、AI医疗等板块,结合涨停梯队和资金动向,建议关注以下方向: 1. 机器人概念(20家涨停) 核心标的:七板龙头杭齿前…...
Linux 实操篇 组管理和权限管理、定时任务调度、Linux磁盘分区和挂载
一、组管理和权限管理 (1)Linux组基本介绍 在linux中的每个用户必须属于一个组,不能独立于组外 在linux中每个文件有所有者、所在组、其他组的概念 (2)文件/目录 所有者 一般为文件的创建者,谁创建了该…...
对CSS了解哪些?
CSS(Cascading Style Sheets,层叠样式表)是用来描述HTML文档外观和布局的语言。以下是对CSS的常见了解范围: 1. CSS 基础 选择器:如通用选择器 (*)、类型选择器、类选择器 (.class)、ID选择器 (#id)、后代选择器、伪类…...
虚拟机新建Ubuntu系统联网快速配置实现能正常ssh连接
1.写一个shell脚本 #!/bin/bash# 更新系统 sudo apt update -y# 安装openssh-server sudo apt install openssh-server -y# 启动SSH服务 sudo systemctl start ssh# 设置SSH服务开机自启 sudo systemctl enable ssh# 检查SSH服务状态 sudo systemctl status ssh# 允许SSH连接 …...
青少年编程都有哪些比赛可以参加
Python小学生可参加的赛事: 电子学会青少年编程考级、中国计算机学会编程能力等级认证、蓝桥杯、 信奥赛CSP-J/S初赛/NOIP(推荐C)、编程设计、信息素养、科技创新赛; 升学助力(科技特长生、大学)、企业、出国留学; python比赛&am…...
MySql中的事务、MySql事务详解、MySql隔离级别
文章目录 一、什么是事务?二、事务四大特性ACID 2.1、原子性(Atomicity)2.2、一致性(Consistency)2.3、隔离性(Isolation)2.4、持久性(Durability) 三、事务操作/事务的…...
10、k8s对外服务之ingress
service和ingress的作用 service的作用 NodePort:会在每个节点开放一个端口,端口号30000-32767。 也是只能用于内网访问,四层转发。实现负载均衡。不能基于域名进行访问。 clusterip:service的默认类型,只能在集群…...
【结束】JS如何不通过input的onInputFileChange使用本地mp4文件并播放,nextjs下放入public文件的视频用video标签无法打开
本地不用input标签获取video视频并播放 浏览器没有像JAVA这些语言之类的IO 代码: <div><video id"video_id" width"750" height"500" controls>Your browser does not support the video tag.</video> </div…...
