Location>code7788 >text

apisix~Custom File Upload Proxy Plugin~Support form-data file and kv parameter

Popularity:624 ℃/2024-10-12 10:53:44

bibliography

  • /questions/24535189/composing-multipart-form-data-with-a-different-content-type-on-each-parts-with-j
  • /r/lua/comments/yaizxv/lua_post_multipartformdata_and_a_file_via/?rdt=60519
  • /rstudio/redx/issues/19

Causes of the problem

The back-end has a file upload service , the front-end can be directly like a file uploaded to the server , but this upload service in addition to the form-data file stream , but also need to have other key/value of the form parameters , these parameters are fixed , or there are certain rules , this time we have a proxy through the apisix , it seems to be more flexible and reason .

The multipart/form-data message body in http is as follows

The modified request, which is a standard http request, as you can also see through postman's codesnippet view, has the following code

POST /mobile-server/manager/6.0.0.0.0/cdnManage/customUpload HTTP/1.1
Host: 
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"


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


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

(data)
----WebKitFormBoundary7MA4YWxkTrZu0gW--

Some pitfalls in the development process

  1. Wrong parameter splicing, the file stream of form-data should be the first parameter.
The server receives an empty request body and parameters
  1. The back-end service reports errors directly for several reasons
  • There are empty boundaries.
  • No \r\n line break between boundary and field
  • Replacing all \n with \r\n may solve the problem of uploading files and parameters that are empty on the receiving end
  • The boundary in the http request header doesn't have the two minus signs at the beginning, which is a very error-prone piece, for example.set_header("Content-Type", "multipart/form-data; boundary=" .. boundary)
  • Boundary is not the same before each field, you need to focus on it, usually starts with ------ to see if the number of - is different, there may be the following error on the receiving end, indicating that the request body is not spliced correctly
Failed to parse multipart servlet request; nested exception is : : Stream ended unexpectedly

file-upload-proxy file upload forwarding plugin source code

-- author: zhangzhanling
-- File Upload Service Agent
-- Proxy Front End,Communicate with the file upload service
-- In the request body,Add harmonized parameters
local core = require("")
local uuid = require("-uuid")
local ngx = require("ngx")
-- Defining the original data format
local schema = {
    type = "object",
    properties = {
        folder = {
            type = "string",
            description = "relative catalog"
        },
        domain = {
            type = "string",
            description = "Domain names for image services"
        }
    }
}

local _M = {
    version = 0.1,
    priority = 1009, --The values are oversized.,The higher the priority,on account ofauthz-keycloakbe2000,It needs to be inauthz-keycloakAfter that, execute the,So make it1000,on account of咱们也依赖proxy_rewriteplug-in (software component)
    name = "file-upload-proxy",
    schema = schema
}

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

end
-- helper function:Find Boundary String
local function find_boundary(content_type)
    return content_type:match("boundary=([^;]+)")
end

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

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

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

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

    local startBoundary = "--" .. boundary

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

    ---- Constructing a new request body
    local new_body = ""

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

    if filename then
        -- through (a gap)filenameExtracting extensions from
        local _, _, ext = (filename, "%.([^.]+)$")
        if ext then
            ("The file extension is: " .. ext)
            fileExt = "." .. ext;
        end
    end

    -- Add new field
    local new_fields = {
        { name = "domain", value = },
        { name = "fileName", value = uuid() .. fileExt },
        { name = "folder", value = folder }
    }
    ---- Add new field
    for _, field in ipairs(new_fields) do
        new_body = new_body .. ("\r\n%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s", startBoundary, , )
    end

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

    -- Setting up a new request body
    .set_body_data(new_body)

    -- update Content-Type beginning or end
    .set_header("Content-Type", "multipart/form-data; boundary=" .. boundary)

    -- calculate and set Content-Length
    local content_length = (new_body)
    .set_header("Content-Length", content_length)

    -- Log output new request body and content length
    ("boundary:", boundary)
    ("New request body: ", new_body)
    ("Content-Length: ", content_length)
end

-- 注册plug-in (software component)
return _M