关于在H5或delphi中使用multipart/form-data上传文件的Rest方法
一、先看官方案例:亚马逊Amazon及微软Azure的CloudAPI云API测试项目
1、主要涉及的单元:
1.1、CloudRefactorUI.pas // ${YourDelphiInstallPath}\Samples\Object Pascal\Database\CloudAPI\CloudAPITest\ //:云API测试UI主界面单元文件
1.1.2、CloudPopulator.pas // ${YourDelphiInstallPath}\Samples\Object Pascal\Database\CloudAPI\CloudAPITest\ //:云API测试单元文件
1.1.3、Data.Cloud.AmazonAPI.pas // ${YourDelphiInstallPath}\source\data\cloud //:delphi云数据之云计算的亚马逊的API翻译为pascal的单元
1.1.4、Data.Cloud.CloudAPI.pas // ${YourDelphiInstallPath}\source\data\cloud //:delphi云数据之云计算的公共单元
2、申请云服务的步骤:
2.1、首先要到云服务商的平台上申请账号Account
2.2、申请云平台提供的服务及其访问参数的描述
2.3、申请服务签名Signature
2.4、阅读服务的API访问和代码调用说明
3、准备架构和代码的步骤:
3.1、架构与设计
3.1.1、获取账号的User ID
以上,有官方代码,自己看!下面我用阿里云通讯短信服务API说明如下(代码部分摘自“高勇三层Rest服务器产品”):
3.2、代码的基本步骤(需要你自己按照云服务提供的云API服务的详细说明,一步一步地去接口实现)
3.2.1、准备所有API访问公用的资源(变量)
3.2.2、签名验证:按API要求获取最终所需的签名字符串Signature
3.2.3、组装服务的Url
3.2.4、请求服务的Url(Get或Post)、获取响应Response、解析返回结果的JSon或XML数据
二、关于multipart/form-data原理
1、什么是multipart/form-data
2、为什么会有multipart/form-data的出现
3、delphi使用TNetHttp体系时解决Rest应用的代码逻辑:
4、参考网友博文:《深入解析 multipart/form-data》 https://blog.csdn.net/wyn126/article/details/96451357
本博客相关:
喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享:
一、先看官方案例:亚马逊Amazon及微软Azure的CloudAPI云API测试项目
${YourDelphiInstallPath}\Samples\Object Pascal\Database\CloudAPI\CloudAPITest\CloudAPITest.dproj
1、主要涉及的单元:
1.1、CloudRefactorUI.pas // ${YourDelphiInstallPath}\Samples\Object Pascal\Database\CloudAPI\CloudAPITest\ //:云API测试UI主界面单元文件
1.1.2、CloudPopulator.pas // ${YourDelphiInstallPath}\Samples\Object Pascal\Database\CloudAPI\CloudAPITest\ //:云API测试单元文件
1.1.3、Data.Cloud.AmazonAPI.pas // ${YourDelphiInstallPath}\source\data\cloud //:delphi云数据之云计算的亚马逊的API翻译为pascal的单元
1.1.4、Data.Cloud.CloudAPI.pas // ${YourDelphiInstallPath}\source\data\cloud //:delphi云数据之云计算的公共单元
2.1.1、获取账号的User ID
2.1.2、获取账号的AK(账号的Json或XML键值对):Account Key
2.1.2、获取账号的SK(密码的Json或XML键值对):Secret Key
2.2、申请云平台提供的服务及其访问参数的描述
2.2.1、老外好像就叫“Buckets”(tmd的翻译过来叫“水桶”)
2.3、申请服务签名Signature
2.3.1、获取需要的服务的签名Signature
2.4、阅读服务的API访问和代码调用说明
2.4.1、API访问和代码调用说明
3、准备架构和代码的步骤:
3.1、架构与设计
3.1.1、获取账号的User ID
以上,有官方代码,自己看!下面我用阿里云通讯短信服务API说明如下(代码部分摘自“高勇三层Rest服务器产品”):
3.2、代码的基本步骤(需要你自己按照云服务提供的云API服务的详细说明,一步一步地去接口实现)
3.2.1、准备所有API访问公用的资源(变量)
3.2.1.1、baseURL
:协议SCHEME(http或https多数云服务要求https) + 特殊保留字Typical scheme '://' + 主机(host域名或IP)
比如阿里云通讯baseURL:
https://dysmsapi.aliyuncs.com
3.2.1.2、SortedParams: TArray<string>;
:云服务的参数字典键值对数组
比如阿里云短信服务的参数字典键值对数组(
以下出斜体注释的部分参数,其余的参数,大部分云服务API参数均大致相同
):
Params
:=
TDictionary<string, string>
.Create;
Params.Add('AccessKeyId', AccessKeyId); //:
AK
(见上面2.1.2的说明)
Params.Add('Timestamp',
GetTimestamp
); //:
时间截
(不同云服务商不同云服务对其格式有特殊要求)及
获取时间截的函数GetTimestamp
Params.Add('Format', 'JSON'); //:
请求和响应的数据格式
(不同云服务商不同云服务对其数据格式有特殊要求:JSON或XML)
Params.Add('SignatureMethod', 'HMAC-SHA1'); //:
签名的加密算法
(不同云服务商不同云服务对其算法有特殊要求)
Params.Add('SignatureNonce', THashMD5.GetHashString(TGUID.NewGuid.ToString)); //:
每次使用都不一样的一次性加密签名字符串
(不同云服务商不同云服务对其有特殊要求:HashMD5常用)
Params.Add('SignatureVersion', '1.0'); //:
签名的版本
Params.Add('Action', 'SendSms'); //:
服务的具体函数
Params.Add('Version', '2017-05-25'); //:
服务的版本
Params.Add('RegionId', 'cn-hangzhou'); //:
服务的区域
Params.Add('PhoneNumbers', PhoneNumbers); //:
其它该服务特有的必备参数
Params.Add('SignName', SignName); //:
申请的服务的签名
Params.Add('TemplateCode', TemplateCode); //:
其它该服务特有的必备参数
Params.Add('TemplateParam', TemplateParam); //:
其它该服务特有的必备参数
Params.Add('OutId', ''); //:
输出的参数或JSON数据
SortedParams
:= Params.keys.ToArray;
TArray.
Sort
<string>(SortedParams);
3.2.1.3、构造待签名的请求查询字符串
StringBuilder := TStringBuilder.Create;
//......代码略
SortedQueryString := StringBuilder.ToString.Substring(1);
3.2.1.4、对待签名的请求串进行URL编码,去掉或替换特殊URL编码字符
TNetEncoding.Url.Encode
3.2.2、签名验证:按API要求获取最终所需的签名字符串Signature
签名,云服务器提供你申请,你的客户端代码肯定也就知道,只是需要服务器和客户端双方进行验证,以保证就是申请人(申请机构)在访问自己账号下的云服务
Signature
3.2.3、组装服务的Url
Url
:= 'https://dysmsapi.aliyuncs.com/?Signature=' +
Signature
+ '&' +
SortedQueryString
;
3.2.4、请求服务的Url(Get或Post)、获取响应Response、解析返回结果的JSon或XML数据
HTTP.ContentType
:= '
application/x-www-form-urlencoded
'; //: HTTP := TNetHTTPClient.Create(nil);
//:这个
HTTP.ContentType
便是本文需要讲述的关于html重要head头信息的重要设置赋值
Response
:= HTTP.
Get
(
Url
).ContentAsString(TEncoding.UTF8);
JsonObj
:= TJSONObject.ParseJSONValue(Response) as TJSONObject;
它是HTML 表单中的编码方式 (Enctype) 之一,有三种类型:
application/x-www-form-urlencoded
==========》对应delphi中的THTTPClient.Post的参数ASource: TMultipartFormData的Mime的类型:TMultipartFormData.Create或TMultipartFormData.AddField或或TMultipartFormData.AddBytes或TMultipartFormData.AddStream的应用程序二进制
如果要发送大量的二进制数据(non-ASCII),application/x-www-form-urlencoded 显然是低效的,因为它需要用 3 个字符来表示一个 non-ASCII 的字符。因此,这种情况下,应该使用 “multipart/form-data” 格式。
multipart/form-data
==========》对应delphi中的THTTPClient.Post的参数ASource: TMultipartFormData的Mime的类型:TMultipartFormData.AddFile的二进制
使用“application / x-www-form-urlencoded”对于发送大量二进制数据或包含非ASCII字符的文本效率低下。“multipart / form-data”应该用于提交包含文件,非ASCII数据和二进制数据的表单。
text-plain
(即:默认的application/x-www-urlencoded) ==========》对应delphi中的Get附在 url 链接后面的字符串或Post网页Body部分的内容;或head方法发送头部请求。
默认情况下是 application/x-www-urlencoded,当表单使用 POST 请求时,数据会被以 x-www-urlencoded 方式编码到 Body 中来传送,而如果 GET 请求,则是附在 url 链接后面来发送。GET 请求只支持 ASCII 字符集,因此,如果我们要发送更大字符集的内容,我们应使用 POST 请求。
HTML提交表单数据:
默认情况下是 application/x-www-urlencoded,当表单使用 POST 请求时,数据会被以 x-www-urlencoded 方式编码到 Body 中来传送,而如果 GET 请求,则是附在 url 链接后面来发送。GET 请求只支持 ASCII 字符集,因此,如果我们要发送更大字符集的内容,我们应使用 POST 请求。
如果要发送大量的二进制数据(non-ASCII),application/x-www-form-urlencoded 显然是低效的,因为它需要用 3 个字符来表示一个 non-ASCII 的字符。因此,这种情况下,应该使用 “multipart/form-data” 格式。
使用“application / x-www-form-urlencoded”对于发送大量二进制数据或包含非ASCII字符的文本效率低下。“multipart / form-data”应该用于提交包含文件,非ASCII数据和二进制数据的表单。
3、delphi使用TNetHttp体系时解决Rest应用的代码逻辑:
delphi的Rest解决方案
:《
delphi Restful:客户端实现的四种方式及其比较
》:
https://blog.csdn.net/pulledup/article/details/104132753
System.Net.HttpClientComponent.pas
==========》 HTTP := TNetHTTPClient.Create(nil); ==========》
关于表单的多个数据对象TMultipartFormData的提交:
1、Post表单的多个数据对象
function Post(const AURL: string; const ASource:
TMultipartFormData
; const AResponseContent: TStream = nil;
const AHeaders:
TNetHeaders
= nil):
IHTTPResponse
; overload;
2、Put表单的多个数据对象
function Put(const AURL: string; const ASource:
TMultipartFormData
; const AResponseContent: TStream = nil;
const AHeaders:
TNetHeaders
= nil):
IHTTPResponse
; overload; ==========》 IHTTPResponse : System.Net.HttpClient.pas
System.Net.HttpClient.pas
==========》 FHttpClient := THTTPClient.Create; FHttpClient.OnReceiveData := DoOnReceiveData; Result := THTTPClient(TURLSchemes.GetURLClientInstance('HTTP')); ==========》
System.Net.URLClient.pas
==========》 FSchemeClients.TryGetValue(AScheme.ToUpper, LClientClass); if LClientClass <> nil then Result := LClientClass.CreateInstance;TURLClient.CreateInstance: TURLClient; TURLClient.SetCustomHeaderValue(const Name, Value: string);
System.Net.Mime.pas
==========》
TMultipartFormData
= class (TObject) //:详见类的Public公开属性和方法
TMimeTypes
= class (TObject) //:详见类的Public公开属性和方法 ===========》HTTP.ContentType := 'MimeType的类型值';
TAcceptValueListBase<T: TAcceptValueItem, constructor>
= class (TObject) //:详见类的Public公开属性和方法
THeaderValueList
= class (TObject) //:详见类的Public公开属性和方法
System.NetConsts.pas
==========》(常量、错误提示)
//: uses System.NetConsts;
const
DefaultUserAgent = 'Embarcadero URI Client/1.0'; // Do not translate
// Common Header Names
sUserAgent = 'User-Agent'; // Do not translate
sAccept = 'Accept'; // Do not translate
sAcceptCharset = 'Accept-Charset'; // Do not translate
sAcceptEncoding = 'Accept-Encoding'; // Do not translate
sAcceptLanguage = 'Accept-Language'; // Do not translate
sAcceptRanges = 'Accept-Ranges'; // Do not translate
sContentEncoding = 'Content-Encoding'; // Do not translate
sContentLanguage = 'Content-Language'; // Do not translate
sContentLength = 'Content-Length'; // Do not translate
sContentType = 'Content-Type'; // Do not translate
sLastModified = 'Last-Modified'; // Do not translate
sContentDisposition = 'Content-Disposition'; // Do not translate
sLocation = 'Location'; // Do not translate
sSetCookie = 'Set-Cookie'; // Do not translate
sCookie = 'Cookie'; // Do not translate
sRange = 'Range'; // Do not translate
sXMethodOverride = 'x-method-override'; // Do not translate
sWWWAuthenticate = 'WWW-Authenticate'; // Do not translate
sProxyAuthenticate = 'Proxy-Authenticate'; // Do not translate
sAuthorization = 'Authorization'; // Do not translate
sProxyAuthorization = 'Proxy-Authorization'; // Do not translat
本博客相关:
1、《
delphi Restful:客户端实现的四种方式及其比较
》
2、
《
RAD Studio 10.4.1的TEdgeBrowser与javascript交互-基于Chromium的Edge浏览器控件用法之二
》
3、
《Delphi Restful之客户端javascript与中间件服务器交互》
喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享:
关于在H5或delphi中使用multipart/form-data上传文件https://blog.csdn.net/wyn126/article/details/96451357
我们都知道要让form能提交
文件
,需要在form上指定enctype=
multipart
/
form-data
的attribute,这样才能上
传
文件
,关于enctype的文章很多,就不再做解释。 问题是因为
使用
了MVC的Html.BeginForm()来输出表单代码,默认是没有加入enctype的, 代码如下: @using (Html.BeginForm()) { } 在PartialView
中
有一个<input type=”file” />用来上
传
文件
,又不想为了这个PartialView去修改父页面的Html.BeginForm(),我的做法是在PartialView
中
用脚本来为for
最近,实现一个网站的登录查询。遇到
multipart
/
form-data
数据提交。
POST /admin/api/common/syslogin HTTP/1.1
Host: xxx.xx.xx.xxx
Connection: keep-alive
Accept: /
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like
开始前,先看下要实现的微信接口,上
传
多媒体
文件
,这个接口是用Form表单形式上
传
的
文件
。对我来说,对http的Form表单一知半解,还好,查到这个资料,如果你也和我一样,必须看看这篇文章。
在xalion窑主的指导下,我
使用
了indy自带的TId
MultiPart
FormDataStre
am类,来提交上
传
的
文件
。
如果
使用
indy的idhttp,则调用这个
方法
,即可以提交For...
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletR
pb
multipart
/
form-data
是一种用于在HTTP请求
中
传
输
文件
和数据的
方法
。它在请求主体
中
使用
分隔线分隔数据,并且每个部分都包含一个标头和一个数据块。其
中
,标头指定了数据块的类型、名称和其他信息,数据块则包含实际上
传
的
文件
内容。
在
使用
pb
multipart
/
form-data
上
传
文件
时,首先需要构建一个HTTP请求,并且指定Content-Type头部为
multipart
/
form-data
。然后,将需要上
传
的
文件
读取为二进制数据,并将其封装成数据块的形式,同时设置好相应的标头信息。如果还有其他需要上
传
的表单数据,也需要封装成相应的数据块形式,并设置好标头信息。
最后,将所有数据块按照分隔线拼接起来,并将拼接后的数据作为请求主体发送到服务器。服务器收到请求后,解析请求主体
中
的数据,并根据标头信息进行相应的处理,从而完成
文件
的上
传
和其他表单数据的提交。
需要注意的是,在
使用
pb
multipart
/
form-data
上
传
文件
时,因为数据需要封装成数据块并进行分隔线拼接,所以相比普通的表单提交,请求主体的大小会更大,且请求处理过程也会更加复杂。因此,在上
传
文件
时,需要谨慎考虑数据大小和性能方面的问题,以避免对服务器造成过大的负担。