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

在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);

}

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值