utils.lua 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. local lfs = require("lfs")
  2. local M = {}
  3. -- 创建目录
  4. M.create_dir = function(dirpath)
  5. -- 检查操作系统类型
  6. local os_type = package.config:sub(1,1)
  7. if os_type == "\\" then
  8. -- Windows 系统
  9. os.execute("mkdir \"" .. dirpath:gsub("/", "\\") .. "\" /p")
  10. else
  11. -- Unix/Linux/Mac 系统
  12. os.execute("mkdir -p \"" .. dirpath .. "\"")
  13. end
  14. end
  15. -- 转换为整数
  16. M.tointeger = function(data)
  17. local num = tonumber(data)
  18. if num == nil then
  19. return nil
  20. end
  21. return math.floor(num)
  22. end
  23. -- 转为时间戳
  24. M.to_timestamp = function(time_str,pattern)
  25. if pattern == nil then
  26. pattern = "(%d+)%-(%d+)%-(%d+)%s+(%d+):(%d+):(%d+)"
  27. end
  28. local y, m, d, h, min, s = time_str:match(pattern)
  29. local timestamp = os.time({
  30. year = tonumber(y),
  31. month = tonumber(m),
  32. day = tonumber(d),
  33. hour = tonumber(h),
  34. min = tonumber(min),
  35. sec = tonumber(s)
  36. })
  37. return timestamp
  38. end
  39. -- 取扩展名
  40. M.ext = function(filepath)
  41. -- 取扩展名
  42. return string.match(filepath,"%.([^.]+)$")
  43. end
  44. -- 复制文件
  45. -- 增加第三个参数 replace,是否替换目标文件,默认为 false
  46. M.copy_file = function(src, dst, replace)
  47. replace = replace or false
  48. -- 检查目标文件是否存在
  49. local dst_file = io.open(dst, "r")
  50. if dst_file ~= nil then
  51. dst_file:close()
  52. if not replace then
  53. return true
  54. end
  55. end
  56. -- 打开源文件
  57. local file = io.open(src, "rb")
  58. if not file then
  59. print("ERR 2,src:",src)
  60. return false
  61. end
  62. local content = file:read("*all")
  63. file:close()
  64. -- 写入目标文件
  65. local file = io.open(dst, "wb")
  66. if not file then
  67. print("ERR 3")
  68. return false
  69. end
  70. file:write(content)
  71. file:close()
  72. return true
  73. end
  74. -- 取路径文件名(带扩展名)
  75. M.filename = function(filepath, have_ext)
  76. local filename = string.match(filepath, "[^/]+$")
  77. if have_ext == false then
  78. local name = string.match(filename, "^(.*)%.") -- get name before last dot
  79. if name ~= nil then
  80. return name
  81. else
  82. return filename -- no extension
  83. end
  84. end
  85. return filename
  86. end
  87. -- 删除文件
  88. M.delete_file = function(filepath)
  89. if os.remove(fw.website_dir()..filepath) ~= true then
  90. return false
  91. end
  92. return true
  93. end
  94. -- 读取文件内容
  95. M.read_file = function(filepath)
  96. local file = io.open(filepath, "rb")
  97. if not file then
  98. return nil, "无法打开文件: " .. tostring(filepath)
  99. end
  100. local content = file:read("*all")
  101. file:close()
  102. return content
  103. end
  104. -- 保存内容到文件
  105. M.save_file = function(filepath, content)
  106. local file = io.open(filepath, "wb")
  107. if not file then
  108. return false, "无法打开文件: " .. tostring(filepath)
  109. end
  110. file:write(content)
  111. file:close()
  112. return true
  113. end
  114. -- 是否存在文件
  115. M.exists_file = function(filepath)
  116. local file = io.open(filepath, "rb")
  117. if file then
  118. file:close()
  119. return true
  120. else
  121. return false
  122. end
  123. end
  124. -- 取文件大小
  125. M.file_size = function(filepath)
  126. local file = io.open(filepath, "rb")
  127. if not file then
  128. return nil
  129. end
  130. local size = file:seek("end")
  131. file:close()
  132. return size
  133. end
  134. -- 取近N个月时间
  135. M.recent_months = function(n)
  136. local mons = {}
  137. local function format_time(y, m, d, h, i, s)
  138. return string.format("%04d-%02d-%02d %02d:%02d:%02d", y, m, d, h, i, s)
  139. end
  140. local now = os.time()
  141. local cur = os.date("*t", now)
  142. for i = n-1, 0, -1 do
  143. local year = cur.year
  144. local month = cur.month - i
  145. while month <= 0 do
  146. month = month + 12
  147. year = year - 1
  148. end
  149. -- 获取该月第一天与最后一天
  150. local first_day = format_time(year, month, 1, 0, 0, 0)
  151. local next_month = month + 1
  152. local next_year = year
  153. if next_month > 12 then
  154. next_month = 1
  155. next_year = year + 1
  156. end
  157. -- next_month 1号的前一天就是当前月最后一天
  158. local last_day_ts = os.time{year=next_year, month=next_month, day=1, hour=0, min=0, sec=0} - 1
  159. local last_day_tm = os.date("*t", last_day_ts)
  160. local last_day = format_time(last_day_tm.year, last_day_tm.month, last_day_tm.day, 23, 59, 59)
  161. local month_str = string.format("%04d-%02d", year, month)
  162. table.insert(mons, month_str)
  163. end
  164. return mons
  165. end
  166. M.recent_months2 = function(n)
  167. local mons = {}
  168. local function format_time(y, m, d, h, i, s)
  169. return string.format("%04d-%02d-%02d %02d:%02d:%02d", y, m, d, h, i, s)
  170. end
  171. local now = os.time()
  172. local cur = os.date("*t", now)
  173. for i = n-1, 0, -1 do
  174. local year = cur.year
  175. local month = cur.month - i
  176. while month <= 0 do
  177. month = month + 12
  178. year = year - 1
  179. end
  180. -- 获取该月第一天与最后一天
  181. local first_day = format_time(year, month, 1, 0, 0, 0)
  182. local next_month = month + 1
  183. local next_year = year
  184. if next_month > 12 then
  185. next_month = 1
  186. next_year = year + 1
  187. end
  188. -- next_month 1号的前一天就是当前月最后一天
  189. local last_day_ts = os.time{year=next_year, month=next_month, day=1, hour=0, min=0, sec=0} - 1
  190. local last_day_tm = os.date("*t", last_day_ts)
  191. local last_day = format_time(last_day_tm.year, last_day_tm.month, last_day_tm.day, 23, 59, 59)
  192. local month_str = string.format("%04d-%02d", year, month)
  193. table.insert(mons, {
  194. month = month_str,
  195. start_time = first_day,
  196. end_time = last_day
  197. })
  198. end
  199. return mons
  200. end
  201. -- 十六进制转字节数组
  202. M.hex_to_bytes = function(hex_str)
  203. -- 移除所有非十六进制字符
  204. hex_str = hex_str:gsub("[^%x]", ""):upper()
  205. -- 补0使长度为偶数
  206. if #hex_str % 2 == 1 then
  207. hex_str = "0" .. hex_str
  208. end
  209. -- 使用 gsub 一次性转换
  210. return (hex_str:gsub("(%x%x)", function(hex)
  211. return string.char(tonumber(hex, 16))
  212. end))
  213. end
  214. --[[
  215. 还原 Redis 字符串(去转义)
  216. @param value 被转义的字符串
  217. @return 返回还原后的字符串
  218. ]]
  219. M.unescape_value = function(value)
  220. if value == nil then
  221. return nil
  222. end
  223. local str = tostring(value)
  224. -- 如果是用双引号包住的,去掉包裹并恢复转义
  225. if #str >= 2 and string.sub(str,1,1) == "\"" and string.sub(str,-1,-1) == "\"" then
  226. str = string.sub(str,2,-2)
  227. str = string.gsub(str, "\\\"", "\"")
  228. str = string.gsub(str, "\\\\", "\\")
  229. return str
  230. end
  231. return str
  232. end
  233. -- 是否为静态资源扩展名
  234. M.is_static_ext_not_html = function(ext)
  235. local static_exts = {
  236. "jpg",
  237. "jpeg",
  238. "png",
  239. "gif",
  240. "bmp",
  241. "ico",
  242. "pdf",
  243. "doc",
  244. "docx",
  245. "xls",
  246. "xlsx",
  247. "ppt",
  248. "pptx",
  249. "txt",
  250. "css",
  251. "js",
  252. "json",
  253. "xml",
  254. "yaml",
  255. "yml",
  256. "zip",
  257. "rar",
  258. "7z",
  259. "tar",
  260. "gz",
  261. "bz2",
  262. "xz",
  263. "mp3",
  264. "wav",
  265. "ogg",
  266. "aac",
  267. "m4a",
  268. "mp4",
  269. "avi",
  270. "mov",
  271. "wmv",
  272. "flv",
  273. "webm",
  274. "mkv",
  275. "avi",
  276. "mov",
  277. "wmv",
  278. "flv",
  279. "webm",
  280. "mkv",
  281. }
  282. for _, v in ipairs(static_exts) do
  283. if v == ext then
  284. return true
  285. end
  286. end
  287. return false
  288. end
  289. -- 遍历目录
  290. M.traverse_dir = function(dirpath)
  291. local files = {}
  292. for file in lfs.dir(dirpath) do
  293. if file ~= "." and file ~= ".." then
  294. table.insert(files, file)
  295. end
  296. end
  297. return files
  298. end
  299. -- 数组是否一致(不考虑顺序)
  300. -- 只考虑 string 和 number/integer 类型,其它类型忽略
  301. M.array_equal = function(a, b)
  302. -- 辅助函数:判断是否为有效类型(string 或 number)
  303. local function is_valid_type(v)
  304. return type(v) == "string" or type(v) == "number"
  305. end
  306. -- 统计数组中有效类型元素的值及其出现次数
  307. local function count_values(arr)
  308. local counts = {}
  309. for _, v in ipairs(arr) do
  310. if is_valid_type(v) then
  311. local key = tostring(v) -- 统一转换为字符串作为key
  312. counts[key] = (counts[key] or 0) + 1
  313. end
  314. end
  315. return counts
  316. end
  317. -- 统计两个数组的有效元素
  318. local counts_a = count_values(a)
  319. local counts_b = count_values(b)
  320. -- 检查 counts_a 中的所有键值对是否在 counts_b 中匹配
  321. for key, count in pairs(counts_a) do
  322. if counts_b[key] ~= count then
  323. return false
  324. end
  325. end
  326. -- 检查 counts_b 中的所有键值对是否在 counts_a 中匹配
  327. for key, count in pairs(counts_b) do
  328. if counts_a[key] ~= count then
  329. return false
  330. end
  331. end
  332. return true
  333. end
  334. -- 处理SQL注入(MySQL 字符串转义)
  335. M.escape_sql = function(str)
  336. if str == nil then
  337. return nil
  338. end
  339. str = tostring(str)
  340. -- 必须先转义反斜杠,再处理其它特殊字符
  341. str = string.gsub(str, "\\", "\\\\")
  342. str = string.gsub(str, "\0", "\\0")
  343. str = string.gsub(str, "\n", "\\n")
  344. str = string.gsub(str, "\r", "\\r")
  345. str = string.gsub(str, "'", "\\'")
  346. str = string.gsub(str, '"', '\\"')
  347. str = string.gsub(str, "\26", "\\Z") -- Ctrl-Z
  348. return str
  349. end
  350. return M