首页 > 编程语言 >post表单数据格式完全解析multipart/form-data(C#实现)

post表单数据格式完全解析multipart/form-data(C#实现)

时间:2022-12-10 16:01:38浏览次数:52  
标签:form C# data -- 数据格式 boundary 换行符 数据

post表单数据格式完全解析multipart/form-data

参数说明

内容

boundary

boundary一个字符串,用以分隔不同的参数;

string boundary = Guid.NewGuid().ToString();

会生成如下字符串:9470b619-f08f-436e-a6b2-98fcb02b695b

也可以自定义一个比较复杂的字符串作为分隔符,例如@adsadsadsads123456789@

request.ContentType = "multipart/form-data;charset=utf-8;boundary=" + boundary;

--boundary开始分隔符,也就是boundary之前加两个短划线,用以标记每一条数据的起始位置;

--boundary--结束分隔符,也就是boundary之前和之后各加两个短划线,用以标记所有数据的结束位置;

 

\r\n

换行符\r\n,用以标记分隔符和参数声明之间、各参数声明之间、参数声明和参数值之间、参数值和下一条数据之间的分隔位置;

--boundary和数据声明之间必须有且只有1个换行符;

各条数据声明之间有且只有1个换行符;

数据声明和数据值之间不少于2个换行符;

数据值和下一条数据之间至少1个换行符;

其他参数

CContent-Type:text/plain;charset=utf-8表示数据内容的格式为普通文本,编码格式为utf-8;

Content-Disposition:form-data表示内容的处置方式,表示将内容作为formdata格式进行处理;

name=\"sInputF\"表示传递参数的名称,或者叫键值、或者字段名称;

所有数据,无论是文本数据还是文件流数据,最终都以byte[]的格式进行传输;

 

示例

--boundary //第一条数据

数据声明1

数据声明2

 

数据

--boundary//第二条数据

数据声明1

数据声明2

 

数据

...

--boundary--//数据结束

 

 

 

结构详解

参数

项目

内容

开始

换行符

第一个开始分隔符前可以没有换行符\r\n

参数1

sInputF

文件参数

开始分隔符

"--" + boundary

换行符

有且只有1个换行符\r\n

数据声明1:

Content-Disposition:form-data;name=\"sInputF\";filename=\"{Path.GetFileName(mDocPath)}\"

换行符

有且只有1个换行符\r\n

数据声明2:

Content-Type:application/octet-stream

换行符

声明和数据值之间,至少要有2个换行符\r\n\r\n

数据值

byte[] docFileBytes = ReadFileBytes(mDocPath);

换行符

至少1个换行符\r\n

参数2

sSealF

文件参数

开始分隔符

"--" + boundary

换行符

有且只有1个换行符\r\n

数据声明1:

Content-Disposition:form-data;name=\"sSealF\";filename=\"{Path.GetFileName(mSealPath)}\"

换行符

有且只有1个换行符\r\n

数据声明2:

Content-Type:application/octet-stream

换行符

声明和数据值之间,至少要有2个换行符\r\n\r\n

数据值

byte[] sealFileBytes = ReadFileBytes(mSealPath);

换行符

至少1个换行符\r\n

参数3

sPageNum

文本参数

开始分隔符

"--" + boundary

换行符

有且只有1个换行符\r\n

数据声明1:

CContent-Type:text/plain;charset=utf-8

换行符

有且只有1个换行符\r\n

数据声明2:

Content-Disposition:form-data;name=\"sPageNum\"

换行符

声明和数据值之间,至少要有2个换行符\r\n\r\n

数据值

1

换行符

至少1个换行符\r\n

参数4

fSealW

文本参数

开始分隔符

"--" + boundary

换行符

有且只有1个换行符\r\n

数据声明1:

CContent-Type:text/plain;charset=utf-8

换行符

有且只有1个换行符\r\n

数据声明2:

Content-Disposition:form-data;name=\"fSealW\"

换行符

声明和数据值之间,至少要有2个换行符\r\n\r\n

数据值

100

换行符

至少1个换行符\r\n

参数5

fSealH

文本参数

开始分隔符

"--" + boundary

换行符

有且只有1个换行符\r\n

数据声明1:

CContent-Type:text/plain;charset=utf-8

换行符

有且只有1个换行符\r\n

数据声明2:

Content-Disposition:form-data;name=\"fSealH\"

换行符

声明和数据值之间,至少要有2个换行符\r\n\r\n

数据值

1

换行符

至少1个换行符\r\n

参数6

fSealPosX

文本参数

开始分隔符

"--" + boundary

换行符

有且只有1个换行符\r\n

数据声明1:

CContent-Type:text/plain;charset=utf-8

换行符

有且只有1个换行符\r\n

数据声明2:

Content-Disposition:form-data;name=\"fSealPosX\"

换行符

声明和数据值之间,至少要有2个换行符\r\n\r\n

数据值

1

换行符

至少1个换行符\r\n

参数7

fSealPosY

文本参数

开始分隔符

"--" + boundary

换行符

有且只有1个换行符\r\n

数据声明1:

CContent-Type:text/plain;charset=utf-8

换行符

有且只有1个换行符\r\n

数据声明2:

Content-Disposition:form-data;name=\"fSealPosY\"

换行符

声明和数据值之间,至少要有2个换行符\r\n\r\n

数据值

1

换行符

至少1个换行符\r\n

结束

结束分隔符

"--" + boundary "--",结束分隔符之后可以没有换行符

源码1

 void Request(string url)

        {

            HttpWebRequest request = WebRequest.CreateHttp(url);//创建request对象,不能通过构造函数创建

            request.Method = "POST";//设置请求方法 GET、HEAD、POST、PUT、DELETE、TRACE 或 OPTIONS

 

            string boundary = Guid.NewGuid().ToString(); // 随机分隔线

            //设置数据类型为multipart/form-data,多部分/数据表单类型,该类型用于传递文件数据的情形,当然也可以传递文本参数

            request.ContentType = "multipart/form-data;charset=utf-8;boundary=" + boundary;

 

            //构建数据,无论文本数据还是文件数据,最终都要以字节流的形式进行发送

 

            //构建数据:word文档文件

            // "--" + boundary表示一条数据的开头

            //第一个"\r\n"表示开始分隔符和数据声明之间的分隔符,二者之间有且只能有1个换行符,如果没有或者超过1个,都将导致数据解析失败

            //Content-Disposition:form-data;name=\"sInputF\";filename=\"{Path.GetFileName(mDocPath)}\"表示一条数据声明

            //Content-Disposition:form-data表示数据的处置方式为表单数据

            //name=\"sInputF\"表示该参数名称、字段名、键

            //filename=\"{Path.GetFileName(mDocPath)}\"上传文件,指明文件名称

            //第二个\r\n表示第一条数据声明和第二条数据声明之间的分隔符,二者之间有且只能有1个换行符,如果没有或者超过1个,都将导致数据解析失败

            //Content - Type:application / octet - stream表示数据类型为字节流

            //最后的\r\n\r\n表示数据声明和数据内容之间的分隔符,二者只有至少有2个换行符,如果换行符超过2个,可以正常解析,如果少于2个,将导致数据解析失败

            string docStr = "--" + boundary + "\r\n" + $"Content-Disposition:form-data;name=\"sInputF\";filename=\"{Path.GetFileName(mDocPath)}\"\r\nContent-Type:application/octet-stream\r\n\r\n\r\n\r\n\r\n";

            byte[] docFileBytes = ReadFileBytes(mDocPath);//从文件中读取字节数据

 

            //构建数据:图片文件

            string sealStr = "--" + boundary + "\r\n" + $"Content-Disposition:form-data;name=\"sSealF\";filename=\"{Path.GetFileName(mSealPath)}\"\r\nContent-Type:application/octet-stream\r\n\r\n";

            byte[] sealFileBytes = ReadFileBytes(mSealPath);//从文件中读取字节数据

 

            //构建数据:普通文本参数

            //每一条数据都已条目分隔符开始,然后后面按照格式拼接相关数据

            //Content-Type: text/plain; charset=utf-8表示内容的格式是普通文本,编码格式为charset=utf-8

            //Content-Disposition:form-data表示数据处理方式,作为表单数据进行处理

            //name表示参数名、字段名、或者键

            //第一个"\r\n",表示开始分隔符和数据声明之间的分隔符,二者之间有且只能有1个换行符,如果没有或者超过1个,都将导致数据解析失败

            //第二个\r\n表示第一条数据声明和第二条数据声明之间的分隔符,二者之间有且只能有1个换行符,如果没有或者超过1个,都将导致数据解析失败

            //最\r\n\r\n表示数据声明和数据内容之间的分隔符,二者只有至少有2个换行符,如果换行符超过2个,可以正常解析,如果少于2个,将导致数据解析失败

            //最后的\r\n表示本条数据内容和下一条数据之间,至少有1个换行符,如果超过1个,可以正常解析,如果没有该换行符,将导致数据解析失败

            string pageStr = "--" + boundary + "\r\n" + $"Content-Type:text/plain;charset=utf-8\r\nContent-Disposition:form-data;name=\"sPageNum\"\r\n\r\n\r\n\r\n\r\n{1}\r\n\r\n\r\n\r\n\r\n\r\n";

            string wStr = "--" + boundary + "\r\n" + $"Content-Type:text/plain;charset=utf-8\r\nContent-Disposition:form-data;name=\"fSealW\"\r\n\r\n{1}\r\n";

            string hStr = "--" + boundary + "\r\n" + $"Content-Type:text/plain;charset=utf-8\r\nContent-Disposition:form-data;name=\"fSealH\"\r\n\r\n{1}\r\n";

            string xStr = "--" + boundary + "\r\n" + $"Content-Type:text/plain;charset=utf-8\r\nContent-Disposition:form-data;name=\"fSealPosX\"\r\n\r\n{1}\r\n";

            string yStr = "--" + boundary + "\r\n" + $"Content-Type:text/plain;charset=utf-8\r\nContent-Disposition:form-data;name=\"fSealPosY\"\r\n\r\n{1}\r\n";

 

            //将所有数据转换为字节数组,并合并在一起

            List<byte> byteList = new List<byte>();

            //第一个文件

            byteList.AddRange(Encoding.UTF8.GetBytes(docStr));//添加文件数据头

            byteList.AddRange(docFileBytes);//添加文件数据

            byteList.AddRange(Encoding.UTF8.GetBytes("\r\n"));//本条数据内容和下一条数据之间,至少有1个换行符,如果没有该换行符,将导致数据解析失败

            //第二个文件

            byteList.AddRange(Encoding.UTF8.GetBytes(sealStr));//添加文件数据头

            byteList.AddRange(sealFileBytes);//添加文件数据

            byteList.AddRange(Encoding.UTF8.GetBytes("\r\n"));//本条数据内容和下一条数据之间,至少有1个换行符,如果没有该换行符,将导致数据解析失败

            //所有的文本参数

            byteList.AddRange(Encoding.UTF8.GetBytes(pageStr));//添加文本参数数据

            byteList.AddRange(Encoding.UTF8.GetBytes(wStr));//添加文本参数数据

            byteList.AddRange(Encoding.UTF8.GetBytes(hStr));//添加文本参数数据

            byteList.AddRange(Encoding.UTF8.GetBytes(xStr));//添加文本参数数据

            byteList.AddRange(Encoding.UTF8.GetBytes(yStr));//添加文本参数数据

            //所有数据的最后,要添加结尾分隔符

            byteList.AddRange(Encoding.UTF8.GetBytes("--" + boundary + "--"));

 

            //设置写入数据的长度,并将数据写入

            request.ContentLength = byteList.Count;//设置数据长度,byte为单位

            Stream postStream = request.GetRequestStream();//获取HttpWebRequest的数据流对象

            postStream.Write(byteList.ToArray(), 0, byteList.Count);//将数据写入,开始位置为0,写入数量为byteList.Count

            postStream.Close();//写完后,关闭流对象

 

 

 

            WebResponse response = request.GetResponse();//发送请求 获取回应

 

            //获取回应的文本内容

            Stream stream = response.GetResponseStream();//获取响应的流

            StreamReader reader = new StreamReader(stream);

            string content = reader.ReadToEnd();//读取响应流

            reader.Close();//关闭流

 

            //构建打印信息

            string showStr = docStr + mDocPath + "\n文件字节数=" + docFileBytes.Length + "\n" + sealStr + mSealPath + "\n文件字节数=" + sealFileBytes.Length + "\n" + pageStr + wStr + hStr + xStr + yStr + "\r\n" + "--" + boundary + "--";

            //显示相关信息

            richTextBox1.Text = "返回信息:\n" + content + "\n\n请求头信息:\n" + request.Headers.ToString() + "发送的数据:\n" + showStr;

 

        }

源码2

  void Request2()

        {

            //创建request对象,不能通过构造函数创建

            HttpWebRequest request = WebRequest.CreateHttp(mPostUrl);

            //设置请求方法 GET、HEAD、POST、PUT、DELETE、TRACE 或 OPTIONS

            request.Method = "POST";

            //获取随机分隔符

            string boundary = Guid.NewGuid().ToString();

            //设置内容格式

            request.ContentType = "multipart/form-data;charset=utf-8;boundary=" + boundary;

            //通过参数字典,获取所有字节数据

            List<byte> byteList = GetAllPostData(mFiles, mParams, boundary);

            //设置内容长度

            request.ContentLength = byteList.Count;

            //获取请求的流对象 并写入字节数据

            Stream postStream = request.GetRequestStream();

            postStream.Write(byteList.ToArray(), 0, byteList.Count);

            postStream.Close();

 

 

            //获取响应

            WebResponse response = request.GetResponse();//发送请求 获取回应

 

            //获取回应的文本内容

            Stream stream = response.GetResponseStream();

            StreamReader reader = new StreamReader(stream);

            string content = reader.ReadToEnd();

            reader.Close();

 

            //显示相关信息

            richTextBox1.Text = "返回信息:\n" + content;

 

        }

 

 //普通参数

        Dictionary<string, string> mParams = new Dictionary<string, string>

        {

            { "sPageNum","1"},

            { "fSealW","100"},

            { "fSealH","100"},

            { "fSealPosX","100"},

            { "fSealPosY","100"},

        };

        //文件参数

        Dictionary<string, string> mFiles = new Dictionary<string, string>

        {

            { "sInputF",mDocPath},

            { "sSealF",mSealPath},

        };

        //获取文件参数的字节数据

        List<byte> GetFilePostData(string field, string path, string boundary)

        {

            List<byte> res = new List<byte>();

            string head = "--" + boundary + "\r\n" + $"Content-Disposition:form-data;name=\"{field}\";filename=\"{Path.GetFileName(path)}\"\r\nContent-Type:application/octet-stream\r\n\r\n";

            res.AddRange(Encoding.UTF8.GetBytes(head));

            res.AddRange(ReadFileBytes(path));

            res.AddRange(Encoding.UTF8.GetBytes("\r\n"));

            return res;

        }

        //获取普通参数的字节数据

        List<byte> GetParamPostData(string field, string value, string boundary)

        {

            List<byte> res = new List<byte>();

            string head = "--" + boundary + "\r\n" + $"Content-Type:text/plain;charset=utf-8\r\nContent-Disposition:form-data;name=\"{field}\"\r\n\r\n{value}\r\n";

            res.AddRange(Encoding.UTF8.GetBytes(head));

            return res;

        }

        //获取所有字节数据

        List<byte> GetAllPostData(Dictionary<string, string> fileDic, Dictionary<string, string> paramDic, string boundary)

        {

 

            List<byte> res = new List<byte>();

            foreach (var item in fileDic)

            {

                List<byte> temp = GetFilePostData(item.Key, item.Value, boundary);

                res.AddRange(temp);

            }

            foreach (var item in paramDic)

            {

                List<byte> temp = GetFilePostData(item.Key, item.Value, boundary);

                res.AddRange(temp);

            }

            res.AddRange(Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n"));

            return res;

        }

        //读取文件中的所有字节

        byte[] ReadFileBytes(string path)

        {

            if (!File.Exists(path)) return new byte[0];

            FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);

            int iLenStream = (int)fs.Length;

            byte[] bArr = new byte[fs.Length];

            fs.Read(bArr, 0, bArr.Length);

            fs.Close();

            return bArr;

        }

标签:form,C#,data,--,数据格式,boundary,换行符,数据
From: https://www.cnblogs.com/zhangdezhang/p/16971725.html

相关文章

  • 现代 CSS 之高阶图片渐隐消失术
    在过往,我们想要实现一个图片的渐隐消失。最常见的莫过于整体透明度的变化,像是这样:<divclass="img"></div>div{width:300px;height:300px;background:url(i......
  • Django之Cookies
    一cookies简述Cookie的由来大家都知道HTTP协议是无状态的。无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请......
  • 在VScode中使用Jupyter Notebook的一些技巧
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • 如何查看CPU和内存使用率
    1、远程登录后,在远程桌面的下边缘有一条横栏叫[任务栏],在[任务栏]空白的地方点击[右键]弹出[右键菜单],选择[任务管理器]或[启动任务管理器]。2、在弹出的[任务管理器]窗口,......
  • excel科学计数法转换成文本完整显示
    1、首先打开含有科学计数法显示的excel;2、在excel最上面的菜单栏点击数据,找到“分列”选项;3、点击“分列“选项,进入分列向导,采用默认选项;4、点击下一步,还是采用默认选项......
  • Object.assign()之浅拷贝解析
    1.做前端开发时遇到一个对象拷贝的问题,当时我把需要拷贝的对象使用Object.assign方法复制了一份,但是再对拷贝出来的对象处理后出现一些问题,如下:constoriginalObject={......
  • Lucas定理
    Lucas定理,是用来求C_m_n%p的C_m_n=C_m%p_n%p*C_m/p_n/p%p;代码:  #include<bits/stdc++.h>usingnamespacestd;longlongt,n,m,p;longlongksm(longlongx,......
  • 开机System recovery怎么删除
    如图,这个开机Systemrecovery怎么删除,每次刷一次系统就多一个,点开又进不去,能删除吗? 解决方法:原因是使用了一键安装系统不成功造成的。开机时onekeyrecovery选项是......
  • Cookie存活时间以及Cookie存储中文
    Cookie存活时间以及Cookie存储中文 cookie在浏览器中保存多长时间:1.默认情况下,当浏览器关闭后,Cookie数据被销毁2.持久化存储:setMaxAge(int seconds)......
  • 金蝶Cosmic虚拟机简单使用与总结
    背景知己知彼简单学习下友商发出来的测试软件看看有否对自己现在的工作有所指导也看看对方的部署方式有啥优缺点当然了仅是测试,不是生产软件可能有失真.注意我没有测试序......