在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. Java 15 转正了,国内几大互联网公司均有贡献,其中腾讯最为突出!
  2. 费解 | 为什么很多程序员工作时都戴耳机?
  3. 思维模型篇:五大生命周期理论
  4. JavaFX官方教程(十五)之A Xylophone.java
  5. IdentityServer4专题之二:OpenID介绍
  6. Python入门(04) -- 函数
  7. MySQL存储过程中的循环怎么写
  8. Java开发笔记(一百二十九)Swing的输入框
  9. ocs边缘服务器部署规划简要说明
  10. 机器视觉九大应用场景
  11. 在android下使用i2c tools
  12. 安卓微软数学(算数,积分,极限,代数)数学神器
  13. 斐讯 K2 路由器 无线中继 无线扩展设置教程图文
  14. ITSM系统核心流程
  15. 赚多多V10自动任务网抢单源码+会员自营版+教程
  16. 基于Arduino uno单片机的仿生螃蟹制作
  17. echarst环形进度,官网https://echarts.apache.org/examples/zh/index.html
  18. ElementUI的Table组件在无数据情况下让“暂无数据”文本居中显示
  19. 使用shiro的会话管理和redis缓存管理来构建登录模块spring+struts+hibernate(SSH)
  20. 电脑读不出u盘解决办法

热门文章

  1. flack框架..学习中
  2. 如何压缩zip格式的文件
  3. Android群英传帝落篇——程序人生,路漫漫其修远兮,吾将上下而求索!
  4. 游戏音乐重要的是让玩家产生共鸣
  5. “未来技术”人工智能算力网络面世:多模态的最佳“伴行者”?
  6. iOS事件全面解析 (触摸事件、手势识别、摇晃事件、耳机线控)
  7. Unity框架之对象池GameObjectPool
  8. c语言课程图书信息管理系统,c语言课程设图书信息管理系统.doc
  9. Android底层开发(3)
  10. jQuery Data 属性