用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.pswp.cn/news/895948.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895948.shtml
英文地址,請注明出處:http://en.pswp.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…