如何通过API请求中的QSYS2.HTTP_POST(_BLOB)_VERBOSE使用表单数据发送大文件?版本7.4
我有一个任务要通过API请求上传一个150MB的 CSV文件。
https://direct.broadsign.com/api/v1/docs/#/Audience/post_api_v1_audience_csv
它的Postman CLI看起来是这样的:
postman request POST 'https://direct.broadsign.com/api/v1/audience/csv'
--header 'Content-Type: text/csv'
--header 'Accept: application/json'
--header 'Cookie: bssta=true; session=.xxxx'
--from 'csv_file=@/ifspath/audience.csv'
这直接来自Broadsign的开发者。
我用Python让它跑起来:
url = "https://direct.broadsign.com/api/v1/audience/csv"
headers = {
"Accept": "application/json",
"Cookie": f"bssta=true; session={token}"
}
with open("audience.csv", "rb") as f:
files = {
"csv_file": ("audience.csv", f, "text/csv")
}
response = requests.post(url, headers=headers, files=files)
我在一个函数里先拿到令牌,能够工作。
现在我必须在IBM i上实现。
我用Copilot和 Claude给出建议,但都没成功。
我找到了这个 RPG API Express ,但它是一个产品,我大概不会买,它应该没有它也能工作,对吗? 总之,我还没找到一个与我的情况匹配的示例。
我现在的情况是这样的
D SNDURL S SQLTYPE(CLOB:2048)
D SNDHDR S SQLTYPE(CLOB:8192)
D SNDBDY S SQLTYPE(CLOB:16773100)
D RSPHDR S SQLTYPE(CLOB:65535) CCSID(1208)
D RSPBDY S SQLTYPE(BLOB_FILE)
D csvFile S SQLTYPE(BLOB_FILE)
D url S 100A
D options s 1000A
url ='https://direct.broadsign.com/api/v1/audience/csv';
options= '{"headers":{"Content-Type":"text/csv",' +
'"Accept":"application/json",' +
'"Cookie":" bssta=true; session='+%TRIM(token) + '"}}';
RSPBDY_Name = '/ifspath/response.txt';
RSPBDY2_NL = %LEN(%TRIMR(RSPBDY_Name));
RSPBDY2_FO = SQFOVR;
csvFile_Name = '/ifspath/audience.csv';
csvFile_NL = %LEN(%TRIMR(csvFile_Name));
csvFile_FO = SQFRD;
SNDURL_DATA = %TRIM(URL);
SNDURL_LEN = %LEN(%TRIMR(SNDURL_DATA));
SNDHDR_DATA = %TRIM(options);
SNDHDR_LEN = %LEN(%TRIMR(SNDHDR_DATA));
exec sql SELECT RESPONSE_MESSAGE,
CAST(RESPONSE_HTTP_HEADER AS CLOB(1000000))
INTO :RSPBDY, :RSPHDR
FROM TABLE( QSYS2.HTTP_POST_BLOB_VERBOSE(
CAST(:SNDURL AS VARCHAR(2048)),
:csvFile,
CAST (:SNDHDR AS CLOB(10K))));
我得到401: Unauthorized,错误说问题出在 "Cookie" 参数。
我去掉 "bssta=true;" 这一部分后,返回400: Bad Request Error。
"The browser (or proxy) sent a request that this server could not understand."
所以不管怎么说,在Postman和 Python里 bssta都不是问题,在IBM i上就是有问题。
我不确定发送文件的方式是不是问题,还是缺少bssta部分,还是我怎样设置BLOB_FILE?
之所以使用BLOB_FILE,是因为CSV文件有150MB。qsys2.HTTP可以发送最多2 GB http_post_blob_verbose Documentation IBM。
我不能在RPG-Field中做到,因为上限只有16MB。正如文档所示,RESPONSE_MESSAGE_DATA也是一个BLOB,因此我也把它定义成一个并且可以在IFS中读取。
也许这就是这里的问题?
我不确定我还缺少什么,另外的问题是我应该把File={"audience.csv",file,"text/csv"} 这部分放在哪里?
主体不能同时包含Blob_file和 字符串。在Python中它是一个元组。
据我所知,"text/csv" 已经在Content-type参数中覆盖,对吗?
任何建议或帮助都将不胜感激。
解决方案
我解决了:
http_post_blob会发送原始二进制。如果我要对接一个期望multipart/form-data的端点,我必须告诉端点它是什么类型的文件。因此,它在该二进制数据中需要一个envelope。
要构造这个envelope你必须写一个boundary。
https://medium.com/@muhebollah.diu/understanding-multipart-form-data-the-ultimate-guide-for-beginners-fd039c04553d
https://www.codelessgenie.com/blog/what-does-webkitformboundary-mean/
就我而言,我的程序在写入CSV内容。在写入数据之前我先写好multipart/forms的头部。
然后是CSV数据本身(表头和行),再用一个尾部收尾,尾部其实就是边界。
如果数据大于16MB,我也使用上述方法,使用blob_file的 sqltype。
该文件应具有1208的 CCSID(UTF8)。
需要改变的是头部中的Content-Type。把 "text/csv" 替换为 "multipart/form-data; boundary=ExampleBoundaryString",并在envelope的头部添加 "Content-Type: text/csv"。
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="csv_file"; filename="audience.csv"
Content-Type: text/csv
Screen ID,Reference ID,Screen Name,Address,Lat/long,Start Date,End Date,Start Time,
End Time,Mon,Tue,Wed,Thu,Fri,Sat,Sun,Total audience/hour
12345,,Test Screen,,,2026-04-21,2026-04-21,00:00:00,00:59:59,0,1,0,,0,0,0,18
12345,,Test Screen,,,,,,,,,,,,,,90
------WebKitFormBoundary7MA4YWxkTrZu0gW--
HTTP头应如下所示:
{"headers":{
"Content-Type":"multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW", "Accept":"application/json","Cookie":"session=xxx"
}}