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
- Wrong parameter splicing, the file stream of form-data should be the first parameter.
The server receives an empty request body and parameters
- 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