插件窝 干货文章 nginx+lua搭建文件上传下载服务问题怎么解决

nginx+lua搭建文件上传下载服务问题怎么解决

file Lua local 文件 638    来源:    2025-04-27

Nginx + Lua 文件上传下载服务解决方案

常见问题及解决方案

1. 文件上传问题

问题: 上传大文件失败或超时

解决方案:

# nginx.conf 配置
http {
    client_max_body_size 100M;  # 设置最大上传文件大小
    client_body_temp_path /tmp/nginx_upload;  # 设置临时文件目录
    client_body_buffer_size 128k;
    client_header_buffer_size 16k;
    large_client_header_buffers 4 16k;

    lua_package_path "/path/to/lua/scripts/?.lua;;";

    server {
        listen 80;

        location /upload {
            content_by_lua_file /path/to/upload.lua;
        }
    }
}
-- upload.lua 示例
local upload = require "resty.upload"
local cjson = require "cjson"

local chunk_size = 4096
local form = upload:new(chunk_size)
form:set_timeout(1000) -- 1秒超时

local file
local file_path
local file_name

while true do
    local typ, res, err = form:read()
    if not typ then
        ngx.say("failed to read: ", err)
        return
    end

    if typ == "header" then
        if res[1]:lower() == "content-disposition" then
            file_name = res[2]:match('filename="(.*)"')
            if file_name then
                file_path = "/path/to/uploads/" .. file_name
                file = io.open(file_path, "w+")
                if not file then
                    ngx.say("failed to open file: ", file_path)
                    return
                end
            end
        end
    elseif typ == "body" then
        if file then
            file:write(res)
        end
    elseif typ == "part_end" then
        if file then
            file:close()
            file = nil
        end
    elseif typ == "eof" then
        break
    end
end

ngx.say(cjson.encode({
    status = "success",
    file_name = file_name,
    file_path = file_path
}))

2. 文件下载问题

问题: 下载速度慢或大文件下载失败

解决方案:

location /download {
    # 使用X-Accel-Redirect实现高效文件下载
    internal;
    alias /path/to/files;

    # 开启断点续传
    proxy_set_header Range $http_range;
    proxy_set_header If-Range $http_if_range;
    proxy_force_ranges on;

    # 优化下载性能
    aio on;
    directio 512;
    output_buffers 1 128k;
}
-- download.lua 示例
local file_path = "/path/to/files/" .. ngx.var.arg_file
local file = io.open(file_path, "rb")

if not file then
    ngx.status = ngx.HTTP_NOT_FOUND
    ngx.say("File not found")
    return
end

local file_size = file:seek("end")
file:close()

ngx.header["Content-Type"] = "application/octet-stream"
ngx.header["Content-Disposition"] = "attachment; filename=" .. ngx.var.arg_file
ngx.header["Content-Length"] = file_size
ngx.header["Accept-Ranges"] = "bytes"

-- 使用X-Accel-Redirect提高性能
ngx.header["X-Accel-Redirect"] = "/download/" .. ngx.var.arg_file

3. 安全性问题

问题: 防止恶意文件上传和非法下载

解决方案:

-- 上传安全验证
local function is_safe_filename(name)
    -- 检查文件扩展名
    local allowed_ext = {jpg=true, png=true, pdf=true, txt=true}
    local ext = name:match("^.+(%..+)$")
    if ext then
        ext = ext:sub(2):lower()
        if not allowed_ext[ext] then
            return false
        end
    end

    -- 防止路径遍历
    if name:find("..") or name:find("/") or name:find("\\") then
        return false
    end

    return true
end

-- 下载权限验证
local function check_download_permission(file_name)
    -- 实现你的权限验证逻辑
    -- 例如检查session、token等
    return true
end

4. 性能优化

配置建议:

# 开启lua代码缓存
lua_code_cache on;

# 使用共享内存存储上传状态
lua_shared_dict upload_status 10m;

# 优化lua执行
lua_socket_log_errors off;
lua_check_client_abort on;

完整示例架构

  1. 目录结构

    /nginx/
     ├── conf/nginx.conf
     ├── lua/
     │   ├── upload.lua
     │   ├── download.lua
     │   └── utils.lua
     ├── uploads/
     └── logs/
    
  2. 依赖安装

    • 安装OpenResty(包含Nginx和LuaJIT)
    • 安装LuaRocks包管理器
    • 安装必要的Lua库:luarocks install lua-resty-upload
  3. 监控与日志

    • 在Lua脚本中添加日志记录
    • 使用Nginx的access_log和error_log
    • 考虑添加Prometheus监控

高级功能扩展

  1. 分片上传

    • 实现大文件分片上传和合并
  2. 文件秒传

    • 基于文件hash检查是否已存在
  3. 下载限速

    • 使用Nginx的limit_rate指令
  4. CDN集成

    • 上传后自动同步到CDN
  5. 病毒扫描

    • 上传后调用病毒扫描API

希望这个解决方案能帮助你搭建稳定高效的文件上传下载服务。根据你的具体需求,可以进一步调整和扩展这些配置。