首页 > 其他分享 >apisix~自定义文件上传代理插件~支持form-data文件和kv参数

apisix~自定义文件上传代理插件~支持form-data文件和kv参数

时间:2024-10-12 09:33:26浏览次数:5  
标签:body 文件 插件 form 自定义 -- data boundary local

参考文献

问题产生的原因

后端有个文件上传服务,前端可以直接像文件上传到服务器,但这个上传服务除了有form-data文件流之外,还需要有其它key/value的表单参数,这些参数是固定的,或者有一定的规则,这时我们通过apisix代理一下,就显得更加灵活和理了。

http中的multipart/form-data消息体如下

修改后的请求,是一个标准的http请求,你通过postman的codesnippet视图也可以看到,代码如下

POST /mobile-server/manager/6.0.0.0.0/cdnManage/customUpload HTTP/1.1
Host: api-gw-test.pkulaw.com
Cookie: CookieId=b97385476b3c721c81a9163f1c8a85dd; SUB=347c9e9e-076c-45e3-be74-c482fffcc6e5; preferred_username=test; session_state=458053bd-5970-4200-9b6f-cf538ec9808b
Content-Length: 508
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="folder"

app/icon
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="domain"

https://static.pkulaw.com
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="fileName"

xzcf.png
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="multipartFile"; filename="/C:/Users/User/Pictures/21111.png"
Content-Type: image/png

(data)
----WebKitFormBoundary7MA4YWxkTrZu0gW--

开发过程中的一些坑

  1. 参数拼接错误,form-data的文件流应该是第一个参数
服务端收到的请求体和参数为空
  1. 后端服务直接报错,原因有以下几个
  • 有空的boundary,
  • boundary与字段之间没有\r\n换行
  • 将所有\n替换为\r\n,可能会解决上传文件和参数在接收端为空的问题
  • http请求头中的boundary是没有开头的两个减号的,这块非常容易出错,例如ngx.req.set_header("Content-Type", "multipart/form-data; boundary=" .. boundary)
  • boundary在各字段之前并不相同,需要着重看一下,一般是------开头,看看是否-的数量不同,可能接收端会有下面的错误,表示请求体拼接不正确
Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: Stream ended unexpectedly

file-upload-proxy文件上传转发插件源码

-- author: zhangzhanling
-- 文件上传服务代理
-- 代理前端,与文件上传服务进行通讯
-- 在请求体中,添加统一的参数
local core = require("apisix.core")
local uuid = require("resty.jit-uuid")
local ngx = require("ngx")
-- 定义原数据格式
local schema = {
    type = "object",
    properties = {
        folder = {
            type = "string",
            description = "相对目录"
        },
        domain = {
            type = "string",
            description = "图片服务的域名"
        }
    }
}

local _M = {
    version = 0.1,
    priority = 1009, --数值超大,优先级越高,因为authz-keycloak是2000,它需要在authz-keycloak之后执行,所以把它定为1000,因为咱们也依赖proxy_rewrite插件
    name = "file-upload-proxy",
    schema = schema
}

local function get_specific_header(ctx, header_name)
    local headers = core.request.headers(ctx)
    local value = headers[header_name]
    if type(value) == "table" then
        return table.concat(value, ", ")
    else
        return value
    end

end
-- 辅助函数:查找边界字符串
local function find_boundary(content_type)
    return content_type:match("boundary=([^;]+)")
end

function _M.rewrite(conf, ctx)
    ngx.req.read_body()
    local body_data = ngx.req.get_body_data()

    if not body_data then
        core.log.warn("Failed to read request body.")
        return 400
    end

    local content_type = ngx.req.get_headers()["content-type"]
    local boundary = find_boundary(content_type)

    if not boundary then
        core.log.warn("No boundary found in content type.")
        return 400
    end

    local startBoundary = "--" .. boundary

    local sub_value = get_specific_header(ctx, "sub")
    local folder = conf.folder
    if sub_value then
        folder = folder .. "/" .. sub_value
    end

    ---- 构建新的请求体
    local new_body = ""

    local fileExt = ".jpg"
    local filename = string.match(body_data, 'filename="([^"]+)"')

    if filename then
        -- 从filename中提取扩展名
        local _, _, ext = string.find(filename, "%.([^.]+)$")
        if ext then
            core.log.info("文件扩展名为: " .. ext)
            fileExt = "." .. ext;
        end
    end

    -- 添加新字段
    local new_fields = {
        { name = "domain", value = conf.domain },
        { name = "fileName", value = uuid() .. fileExt },
        { name = "folder", value = folder }
    }
    ---- 添加新字段
    for _, field in ipairs(new_fields) do
        new_body = new_body .. string.format("\r\n%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s", startBoundary, field.name, field.value)
    end

    new_body = new_body .. "\r\n" .. body_data

    -- 设置新的请求体
    ngx.req.set_body_data(new_body)

    -- 更新 Content-Type 头
    ngx.req.set_header("Content-Type", "multipart/form-data; boundary=" .. boundary)

    -- 计算并设置 Content-Length
    local content_length = string.len(new_body)
    ngx.req.set_header("Content-Length", content_length)

    -- 日志输出新请求体和内容长度
    core.log.warn("boundary:", boundary)
    core.log.warn("New request body: ", new_body)
    core.log.warn("Content-Length: ", content_length)
end

-- 注册插件
return _M

标签:body,文件,插件,form,自定义,--,data,boundary,local
From: https://www.cnblogs.com/lori/p/18459845

相关文章

  • 文件工具类 - C#小函数类推荐
          此文记录的是文件工具类。/***文件工具类AustinLiu刘恒辉ProjectManagerandSoftwareDesignerE-Mail:[email protected]:http://lzhdim.cnblogs.comDate:2024-01-1515:18:00***/namespaceLzhdim.LPF.Utility......
  • 【C#】文件操作
    创建文件夹if(!Directory.Exists(desPath)){try{Directory.CreateDirectory(desPath);}catch(Exceptione){thrownewException("创建目标目录失败:"+e.Message);}}创建文件using(System.IO.StreamWriterfile=new......
  • 文件管理方案参考 2024.10.12
    文件管理方案参考2024.10.12说明:此文档中的文件是指手机、平板电脑、笔记本电脑等电子设备在使用过程中新建、接收、重命名、移动、编辑的电子文件。例如:Word文档(.docx)、Excel表格(.xlsx)、Photoshop图片(.jpg)、酷我音乐盒无损音乐歌曲(.flac)、国语中字电影视频(.MP4)、视频教程(.AVI)。......
  • 常见的内外网文件传输方法 能进前5强的是这几个!
    内外网文件传输主要解决哪些问题?内外网文件传输是指不同隔离网间的文件传输,有些是隔离了内外两个网络,有些会在内部再隔离出多个子网,比如研发网、生产网、测试网等,也可以叫做红区、绿区等安全区域。隔离网间的文件传输主要解决数据安全、传输效率和兼容性问题。一般来说,医院、银......
  • 好用的文件备份系统,一键安装无人值守、离线、实时、定时、跨端、多备份目标类别,雪里备
    一、引言在当今这个信息如潮水般涌动的时代,数据已成为企业和个人不可或缺的资产。然而,数据丢失的风险无处不在,无论是硬件故障、人为错误还是网络攻击,都可能让宝贵的数据瞬间化为乌有。在这样的背景下,拥有一款好用的文件备份系统显得尤为重要。雪里备份系统,正是为了解决这......
  • 最新Qt6将可执行文件打包为独立exe保姆级教学!含报错:无法定位程序输入点于动态链接库解
     相信大家都有类似的体验,自己已经在Qt练习中写出了不错的小程序,每次想发给别人体验都要发一整个大代码包,还得对面有对应的装好的QT才能运行,或者是想把自己的成果记录下来作为一个单独的exe文件却没有办法,今天教大家Qt如何生成独立可执行exe。注意:以下是Qt6之前版本可用的全过......
  • 3款逆天级Word插件,一键解决文档排版烦恼
    在当今快节奏的工作环境中,高效的文档处理能力至关重要,今天电脑天空将为大家介绍三款卓越的Word插件,它们能显著提升你的写作效率,让您的工作成果更加出色。1.文档排版利器:小恐龙公文排版助手小恐龙公文排版助手是一款功能全面的Word和WPS插件,专门针对公文和各类正式文档的排......
  • [转]一文讲透为Power Automate for Desktop (PAD) 实现自定义模块 - 附完整代码
    本文转自:一文讲透为PowerAutomateforDesktop(PAD)实现自定义模块-附完整代码-陈希章-博客园(cnblogs.com) 概述PowerAutomateforDesktop(以下简称PAD)是微软推出的一款针对Windows桌面端的免费RPA(机器人流程自动化)工具,它目前默认会随着Windows11安装,但也可以......
  • Linux文件和文件夹操作
    一、文件操作(一)文件创建命令行作用vi/opt/learn/1.txt在目录/opt/learn下创建1.txt并进入vi界面touch/opt/learn/test在目录/opt/learn下创建空白文件testcat>/opt/learn/catfile创建文件catfile并在屏幕上输入内容,最后按Crtl+D退出(二)文件查看命令行作用vi/etc/pa......
  • 数据库中的数据导入到文件
    将DB2数据库中的表数据导出到DBF(dBase)文件格式并不是DB2本身直接支持的操作,因为DBF文件是dBase数据库使用的旧格式。不过,你可以通过一些间接的方法来完成这个任务。这里提供一种方法,即先将DB2表的数据导出为CSV格式,然后再使用工具将CSV文件转换为DBF文件。 ......