用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/;
}

效果

在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/895948.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LLaMA 3.1 模型在DAMODEL平台的部署与实战:打造智能聊天机器人

文章目录 前言 一、LLaMA 3.1 的特点 二、LLaMA3.1的优势 三、LLaMA3.1部署流程 &#xff08;一&#xff09;创建实例 &#xff08;二&#xff09;通过JupyterLab登录实例 &#xff08;3&#xff09;部署LLaMA3.1 &#xff08;4&#xff09;使用教程 总结 前言 LLama3…

【Python爬虫(25)】解锁Python爬虫:数据存储的最优选择与高效策略

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…

【复现DeepSeek-R1之Open R1实战】系列8:混合精度训练、DeepSpeed、vLLM和LightEval介绍

这里写目录标题 1 混合精度训练1.1 FP16和FP321.2 优点1.3 存在的问题1.4 解决办法 2 DeepSpeed3 vLLM3.1 存在的问题3.2 解决方法3.2.1 PagedAttention3.2.2 KV Cache Manager3.2.3 其他解码场景 3.3 结论 4 LightEval4.1 主要功能4.2 使用方法4.3 应用场景 本文继续深入了解O…

使用 FFmpeg 剪辑视频指南

FFmpeg 是一个功能强大的多媒体处理工具&#xff0c;可以进行视频和音频的剪辑、合并、转码等操作。本文将详细介绍如何使用 FFmpeg 进行视频剪辑&#xff0c;并通过实例帮助你快速掌握剪辑技巧。我们会从最基础的剪切功能讲起&#xff0c;再延伸到一些高级操作&#xff0c;如指…

【分布式理论15】分布式调度1:分布式资源调度的由来与过程

文章目录 一、操作系统的资源调度&#xff1a;从单核到多核二、 分布式系统的资源调度&#xff1a;从单台服务器到集群三、 固定资源映射四、 动态资源分配&#xff1a;灵活的任务-资源匹配五、 资源调度过程&#xff1a;从申请到执行 本文主要讨论主题&#xff1a; 从操作系统…

【Linux C/C++开发】Linux系统轻量级的队列缓存mqueue

前言 开发设计时&#xff0c;通常会对业务流程进行模块化&#xff0c;有些流程之间&#xff0c;不要求同步&#xff0c;但又需要传递信息时&#xff0c;如果存储到数据库&#xff0c;效率降低很多&#xff0c;如果是存放在内存是最好的。此时可以选择系统的IPC&#xff08;进程…

Vue 实现通过URL浏览器本地下载 PDF 和 图片

1、代码实现如下&#xff1a; 根据自己场景判断 PDF 和 图片&#xff0c;下载功能可按下面代码逻辑执行 const downloadFile async (item: any) > {try {let blobUrl: any;// PDF本地下载if (item.format pdf) {const response await fetch(item.url); // URL传递进入i…

计算机网络基础杂谈(局域网、ip、子网掩码、网关、DNS)

目录 1. 简单局域网的构成 2. IP 地址 3. 子网掩码 4. IP地址详解自定义IP 5. IP 地址详解 6. 网关 7. DNS 域名解析 8. ping 1. 简单局域网的构成 交换机是组建局域网最重要的设备&#xff0c;换句话说&#xff0c;没有交换机就没法搭建局域网 交换机不能让局域网连…

Thor: 统一AI模型网关的革新之选

项目价值 Thor(雷神托尔)作为一个强大的AI模型管理网关&#xff0c;解决了当前AI领域一个关键痛点&#xff1a;不同AI服务商的API格式各异&#xff0c;集成成本高。Thor通过将各种AI模型的独特格式统一转换为OpenAI格式&#xff0c;显著降低了开发者的使用门槛和维护成本。 核…

25年2月通信基础知识补充:多普勒频移与多普勒扩展、3GPP TDL信道模型

看文献过程中不断发现有太多不懂的基础知识&#xff0c;故长期更新这类blog不断补充在这过程中学到的知识。由于这些内容与我的研究方向并不一定强相关&#xff0c;故记录不会很深入请见谅。 【通信基础知识补充7】25年2月通信基础知识补充1 一、多普勒频移与多普勒扩展傻傻分不…

【Python】Python入门——笔记合集

哈哈 00、环境搭建 学习Python&#xff0c;首先需要搭建一个本地开发环境&#xff0c;或是使用线上开发环境&#xff08;各类练习网站&#xff09;&#xff0c;这篇博客里主要记录了本地开发环境的配置方法。内容包括python解释器的安装以及pycharm的安装、汉化等。 博客地…

为什么mvcc中?m_ids 列表并不等同于 min_trx_id 和 max_trx_id 之间的所有事务 ID

首先我们要明确 m_ids 表示创建 ReadView 时&#xff0c;系统中所有活跃&#xff08;未提交&#xff09;事务的事务 ID 列表。 仅包含当前未提交的事务&#xff0c;与事务 ID 的数值范围无关。 min_trx_id 是 m_ids 中的最小值。若 m_ids 为空&#xff0c;则 min_trx_id 等于…

使用 Spark NLP 实现中文实体抽取与关系提取

在自然语言处理(NLP)领域,实体抽取和关系提取是两个重要的任务。实体抽取用于从文本中识别出具有特定意义的实体(如人名、地名、组织名等),而关系提取则用于识别实体之间的关系。本文将通过一个基于 Apache Spark 和 Spark NLP 的示例,展示如何实现中文文本的实体抽取和…

FPGA开发要学些什么?如何快速入门?

随着FPGA行业的不断发展&#xff0c;政策的加持和投入的研发&#xff0c;近两年FPGA行业的薪资也是水涨船高&#xff0c;一些人转行后拿到了薪资30W&#xff0c;甚至有一些能力强的人可以拿到60W&#xff0c;看到这里想必不少人表示很心动&#xff0c;但又不知道怎么转&#xf…

使用Python和正则表达式爬取网页中的URL数据

在数据抓取和网络爬虫开发中&#xff0c;提取网页中的URL是一个常见的需求。无论是用于构建网站地图、分析链接结构&#xff0c;还是进行内容聚合&#xff0c;能够高效地从HTML文档中提取URL都是一个重要的技能。Python作为一种强大的编程语言&#xff0c;结合其正则表达式模块…

人工智能之目标追踪DeepSort源码解读(yolov5目标检测,代价矩阵,余弦相似度,马氏距离,匹配与预测更新)

要想做好目标追踪,须做好目标检测,所以这里就是基于yolov5检测基础上进行DeepSort,叫它为Yolov5_DeepSort。整体思路是先检测再追踪,基于检测结果进行预测与匹配。 一.参数与演示 这里用到的是coco预训练人的数据集&#xff1a; 二.针对检测结果初始化track 对每一帧数据都输出…

C++蓝桥杯基础篇(四)

片头 嗨~小伙伴们&#xff0c;大家好&#xff01;今天我们来学习C蓝桥杯基础篇&#xff08;四&#xff09;&#xff0c;继续练习相关习题。准备好了吗&#xff1f;咱们开始咯~ 题目1 连续整数相加 思路分析&#xff1a; 这道题&#xff0c;我们可以把从键盘中读取n写在while循…

YOLOv12从入门到入土(含结构图)

论文链接&#xff1a;https://arxiv.org/abs/2502.12524 代码链接&#xff1a;https://github.com/sunsmarterjie/yolov12 文章摘要&#xff1a; 长期以来&#xff0c;增强YOLO框架的网络架构一直至关重要&#xff0c;但一直专注于基于cnn的改进&#xff0c;尽管注意力机制在建…

SpringSecurity基于配置方法控制访问权限:MVC匹配器、Ant匹配器

Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。在 Spring Security 中&#xff0c;可以通过配置方法来控制访问权限。认证是实现授权的前提和基础&#xff0c;在执行授权操作前需要明确目标用户&#xff0c;只有明确目标用户才能明确它所具备的角色和权…

【iOS】SwiftUI状态管理

State ObservedObject StateObject 的使用 import SwiftUIclass CountModel: ObservableObject {Published var count: Int 0 // 通过 Published 标记的变量会触发视图更新init() {print("TimerModel initialized at \(count)")} }struct ContentView: View {State…