在HTTP服务应用中进行数据提交一般都使用application/json,application/x-www-form-urlencoded和multipart/form-data这几种内容格式。这几种格式的处理复杂度处理起来和前面定义的先后顺序一样由易到难。不过现有工具都提供了完善的功能在提交这些数据的时候都比较方便了;不过要自己手动基础协议写起,那multipart/form-data的处理规范还是要相对复杂些。最近在写webapi管理和性能测试工具(https://github.com/IKende/WebBenchmark)时为了得到更可控的时间线和性能,在实现并没有用到任何应用组件都是从HTTP基础协议写起,在这时介绍一下如何在基础HTTP协议的基础上提交multipart/form-data数据.(如果你没有什么特别的需求还是不要这么干)

multipart/form-data

这种格式一般配合多数据类型提交使用,如常用的数据表单和文件结合。这种格式有着自己的处理规范和application/json和application/x-www-form-urlencoded有着不同。application/json相对来说最简单整个数据流是json内容格式,而application/x-www-form-urlencoded则是以k-v的方式处理,只是对应的值要做Url编写。而multipart/form-data则用一个特别的分隔符来处理,这个分隔符分为开始分隔和结束分隔符。

分隔符定义

如果使用multipart/form-data提交数据,那必须在Content-Type的请求头后面添加; boundary=value这样一个描述,boundary的值即是每项数据之间的分隔符

mHeaderCached.Append("Content-Type:").Append(mCases.ContentType);if(multipartFormData)

mHeaderCached.Append("; boundary=").Append(boundary);

mHeaderCached.Append("\r\n");

需要怎样定义boundary值?其实boundary的定义是没有特别的要求的,就是一个字符串完全看自己的喜好。但最终处理的时候是要有一个规范。

开始分隔符--boundary

结束分隔符--boundary--

开始分隔符必须在每项数据之前写入,简单来说就是有多少项数据就有多少个开始分隔符了;结束分隔符只有一个,就是在提交内容的尾部添加,说明这个提交的内容在这里结束不需要再往下解释。大概格式如下:

-- boundary

数据项

-- boundary

数据项

-- boundary

数据项

-- boundary

数据项

--boundary--

数据项

multipart/form-data中的每项数据都分别有Header和Body和整个HTTP上层协议差不多。

Content-Disposition: form-data; name="fname"\r\n

\r\n

value

\r\n

Content-Disposition是必须的,描述一下这数据的格式来源,在这里都是form-data;后面根据不同数据的情况有着不同的属性,每个属性用;分隔的K-V结构。代码的处理比较简单:

mMemoryData.WriteLine($"Content-Disposition: form-data; name=\"{item.Name}\"");

接下来就是一个空换行然后再写入值,完整代码如下:

mMemoryData.WriteLine($"Content-Disposition: form-data; name=\"{item.Name}\"");

mMemoryData.WriteLine("");

mTextBodyCached.Clear();

item.GetTemplate().Execute(mTextBodyCached);

mMemoryData.Write(mTextBodyCached);

mMemoryData.WriteLine("");

提交文件

提交文件相对来说比值要处理多一些属性,主要包括内容类型,文件名等;其实写起来也不复杂

mMemoryData.WriteLine($"Content-Disposition: form-data; name=\"{item.Name}\"; filename=\"{item.FileName}\"");

mMemoryData.WriteLine($"Content-Type: {item.Type}");

mMemoryData.WriteLine("");var itemBuffer =item.GetBuffer();

mMemoryData.Write(itemBuffer,0, itemBuffer.Length);

mMemoryData.WriteLine("");

以上就是multipart/form-data普通值和文件提交时写的数据格式,下面看一下这个multipart/form-data的完整代码

for (int i = 0; i < mCases.FormBody.Count; i++)

{var item =mCases.FormBody[i];

mMemoryData.Write("--");

mMemoryData.WriteLine(boundary);if (item.Type ==HttpDataType.Bytes)

{

mMemoryData.WriteLine($"Content-Disposition: form-data; name=\"{item.Name}\"; filename=\"{item.FileName}\"");

mMemoryData.WriteLine($"Content-Type: {item.Type}");

mMemoryData.WriteLine("");var itemBuffer =item.GetBuffer();

mMemoryData.Write(itemBuffer,0, itemBuffer.Length);

mMemoryData.WriteLine("");

}else{

mMemoryData.WriteLine($"Content-Disposition: form-data; name=\"{item.Name}\"");

mMemoryData.WriteLine("");

mTextBodyCached.Clear();

item.GetTemplate().Execute(mTextBodyCached);

mMemoryData.Write(mTextBodyCached);

mMemoryData.WriteLine("");

}

}if (mCases.FormBody.Count > 0)

{

mMemoryData.Write("--");

mMemoryData.Write(boundary);

mMemoryData.WriteLine("--");

mMemoryData.Flush();

}

这样一个完整的multipart/form-data提交基础协议代码就处理完成;在webbenchmark的实现有还有application/json和application/x-www-form-urlencoded的处理,相对于multipart/form-data来说这两个处理就更加简单了;下面包括:POST,GET,PUT,DELETE和三种数据格式提交的完整代码函(在BeetleX的pipestream帮助下这些协议的处理还是比较简单的)

public void Write(PipeStream stream)

{

string boundary = null;

bool multipartFormData = mCases.ContentType == "multipart/form-data";

if (multipartFormData)

boundary = "----Beetlex.io" + DateTime.Now.ToString("yyyyMMddHHmmss");

byte[] bodyData = null;

int bodyLength = 0;

if (mHeaderCached == null)

mHeaderCached = new StringBuilder();

mHeaderCached.Clear();

if (mMemoryData == null)

mMemoryData = new PipeStream();

if (mMemoryData.Length > 0)

mMemoryData.ReadFree((int)mMemoryData.Length);

if (mTextBodyCached == null)

mTextBodyCached = new StringBuilder();

mTextBodyCached.Clear();

mHeaderCached.Append(mCases.Method).Append(" ");

mUrlTemplate.Execute(mHeaderCached);

for (int i = 0; i < mCases.QueryString.Count; i++)

{

if (i == 0)

{

if (mUrlHasParameter)

mHeaderCached.Append("&");

else

mHeaderCached.Append("?");

}

else

{

mHeaderCached.Append("&");

}

mHeaderCached.Append(mCases.QueryString[i].Name);

mHeaderCached.Append("=");

mCases.QueryString[i].GetTemplate().Execute(mHeaderCached, true);

}

mHeaderCached.Append(" ");

mHeaderCached.Append(Protocol).Append("\r\n");

foreach (var item in mCases.Header)

{

mHeaderCached.Append(item.Name).Append(": ");

item.GetTemplate().Execute(mHeaderCached);

mHeaderCached.Append("\r\n");

}

mHeaderCached.Append("Content-Type: ").Append(mCases.ContentType);

if (multipartFormData)

mHeaderCached.Append("; boundary=").Append(boundary);

mHeaderCached.Append("\r\n");

if (multipartFormData)

{

for (int i = 0; i < mCases.FormBody.Count; i++)

{

var item = mCases.FormBody[i];

mMemoryData.Write("--");

mMemoryData.WriteLine(boundary);

if (item.Type == HttpDataType.Bytes)

{

mMemoryData.WriteLine($"Content-Disposition: form-data; name=\"{item.Name}\"; filename=\"{item.FileName}\"");

mMemoryData.WriteLine($"Content-Type: {item.Type}");

mMemoryData.WriteLine("");

var itemBuffer = item.GetBuffer();

mMemoryData.Write(itemBuffer, 0, itemBuffer.Length);

mMemoryData.WriteLine("");

}

else

{

mMemoryData.WriteLine($"Content-Disposition: form-data; name=\"{item.Name}\"");

mMemoryData.WriteLine("");

mTextBodyCached.Clear();

item.GetTemplate().Execute(mTextBodyCached);

mMemoryData.Write(mTextBodyCached);

mMemoryData.WriteLine("");

}

}

if (mCases.FormBody.Count > 0)

{

mMemoryData.Write("--");

mMemoryData.Write(boundary);

mMemoryData.WriteLine("--");

mMemoryData.Flush();

}

}

else if (mCases.ContentType == "application/json")

{

if (mJsonBodyTemplate != null)

{

mJsonBodyTemplate.Execute(mTextBodyCached);

}

}

else

{

for (int i = 0; i < mCases.FormBody.Count; i++)

{

if (i > 0)

{

mTextBodyCached.Append("&");

}

mTextBodyCached.Append(mCases.FormBody[i].Name).Append("=");

mCases.FormBody[i].GetTemplate().Execute(mTextBodyCached, true);

}

}

try

{

if (multipartFormData)

{

bodyLength = (int)mMemoryData.Length;

if (bodyLength > 0)

{

bodyData = System.Buffers.ArrayPool.Shared.Rent(bodyLength);

mMemoryData.Read(bodyData, 0, bodyLength);

}

}

else

{

if (mTextBodyCached.Length > 0)

{

char[] charbuffer = System.Buffers.ArrayPool.Shared.Rent(mTextBodyCached.Length);

try

{

mTextBodyCached.CopyTo(0, charbuffer, 0, mTextBodyCached.Length);

bodyData = System.Buffers.ArrayPool.Shared.Rent(mTextBodyCached.Length * 6);

bodyLength = Encoding.UTF8.GetBytes(charbuffer, 0, mTextBodyCached.Length, bodyData, 0);

}

finally

{

System.Buffers.ArrayPool.Shared.Return(charbuffer);

}

}

}

mHeaderCached.Append("Content-Length: ").Append(bodyLength).Append("\r\n");

mHeaderCached.Append("\r\n");

stream.Write(mHeaderCached);

if (bodyData != null)

{

stream.Write(bodyData, 0, bodyLength);

}

}

finally

{

if (bodyData != null)

System.Buffers.ArrayPool.Shared.Return(bodyData);

}

}

java multipart/form-data 提交_从零开始实现multipart/form-data数据提交相关推荐

  1. java怎么制作放置游戏_从零开始实现放置游戏(八)——实现后台管理系统(6)代码重构...

    前几张,我们主要实现了升级经验.人物等级属性.地图.地图怪物,这四种配置的增删查改以及Excel导入功能.我们主要以地图怪物为例,因此在文章末尾提供的源代码中只实现了地图怪物这部分的逻辑功能. 如果你 ...

  2. java怎么制作放置游戏_从零开始实现放置游戏(前言)

    笔者从小学就开始打电脑游戏,大学读的是软件工程,毕业后工作内容涉及电商.金融.运维等领域,却一直未曾开发过游戏.作为一名游戏爱好者,始终是心中的一个遗憾. 然而,随着时代的变迁,出现了越来越多好用的工 ...

  3. java 多线程合并结果集_多线程计算数据,然后合并数据

    假设有一个计算量非常大的任务,使用单线程处理会花费很长时间才能处理完成,这时候可以考虑使用多线程分批计算数据,然后再汇总数据输出.在这里,使用了CyclicBarrier来实现.这个类的功能就是指定特 ...

  4. c语言如何将一个二维数组全为零_从零开始如何用python处理数据

    这是一篇教程类的博客,面向的是非计算机专业的同学,所以比较基础 1. 使用语言和包 这里我用的是python进行处理.So, why python? Python语法相对简单,不是程序员的话,当做工具 ...

  5. java中poi搜索工程_爬取高德地图poi数据

    高德地图搜索poi的api介绍地址 当前想法是爬取目标区域(作者所在小县城)的所有poi数据,存到数据库中作为原始数据,然后供其它系统调用,因为之前爬取过百度地图的poi数据,所以这次工作就驾轻就熟了 ...

  6. 织梦主动提交_织梦CMS网站如何自动提交百度快速收录(详细教程)

    前不久,站长们发现百度搜索资源平台把链接提交"的功能改为"普通收录",同时新增了"快速收录"的功能.普通收录,我相信很多站长们都知道怎么去提交,但是快 ...

  7. java stream 多次读取_多次从具有大量数据的Java InputStream中读取

    我想知道什么是从Java InputStream多次读取字节并在流很大时仍然有效的最佳方法. 假设我有以下代码: public void handleBytes(InputStream in) { d ...

  8. springboot 订单重复提交_瞬间几千次的重复提交,我用Spring Boot+Redis扛住了

    在实际的开发项目中,一个对外暴露的接口往往会面临,瞬间大量的重复的请求提交,如果想过滤掉重复请求造成对业务的伤害,那就需要实现幂等! 我们来解释一下幂等的概念: 任意多次执行所产生的影响均与一次执行的 ...

  9. mysql 读取data文件_利用mysql的LOAD DATA INFILE的功能读取客户端文件

    前言:今天在浏览某知论坛时,看到某大佬在渗透过程中使用伪造的MySQL服务端读取客户端文件,此大佬在利用过程中描述得不是很详细,作为小白的我看不懂啊,由此产生了此篇文章. 某大佬文章:https:// ...

最新文章

  1. c++ 解析从浏览器端传过来的图像base64编码,并转换成opencv识别的格式
  2. 剑网3 最新服务器,《剑网3》各服务器IP及所在地
  3. 【转】图形学基础之透视校正插值
  4. AIX忘记root密码后,重设密码步骤
  5. Teams AppId, InstallationId 和 ExternalId 的区别
  6. 获取随机数的方式Random类对象的方法 java 0913
  7. 怎么用python输入计算公式_如何从python3中的用户输入计算min和max
  8. CNN更新换代!性能提升算力减半,还即插即用
  9. centosx64 6.2安装virtualbox
  10. 在春天,我用秋来诱惑你
  11. GlusterFS更换故障Brick
  12. 随机数练习1,和电脑比roll点
  13. 一台计算机连接两个投影,Win10系统电脑外接双显示器(投影仪)设置图文教程
  14. j2ee与j2se对比
  15. 微信小程序选择地址填写详细地址定位地点
  16. Quartus (Quartus Prime 18.1)的安装及仿真步骤
  17. Java 写数据到文件
  18. linux0.11-文件系统
  19. 华为ENSP之 MPLS V Extranet
  20. 和月薪5W的京东程序员聊过后,才知道自己一直在打杂...

热门文章

  1. 牛客网python刷题_牛客网刷题
  2. java实现第七届蓝桥杯有奖竞猜
  3. 量水堰槽水位计的用途和使用方法说明
  4. 2020华南师范计算机电子信息专硕双非上岸经验贴
  5. iOS /OC之视频、音频、相机、相册综合
  6. ZooKeeper 避坑实践:如何调优 jute.maxbuffer
  7. 计算机任务栏出现地址框,任务栏位置怎么调整【图文】
  8. Cookies 处理
  9. 转:洛克菲勒写给儿子的三封家书:利益、精神、尊严
  10. 网易云音乐项目————项目准备