zoukankan      html  css  js  c++  java
  • 【转】asp.net(c#)使用HttpWebRequest附加携带请求参数以post方式模拟上传大文件(以图片为例)到Web服务器端

    原文地址:http://docode.top/Article/Detail/10002

    目录:

    1、Http协议上传文件(以图片为例)请求报文体内容格式

    2、完整版HttpWebRequest模拟上传文件请求报文内容封装

    3、asp.net(c#)使用HttpWebRequest携带请求参数模拟上传文件封装源码下载

    一、Http协议上传文件(以图片为例)请求报文体内容格式

        首先,我们来看下通过浏览器上传文件的请求报文内容格式,这里以本人自己写的实例为例,如下图。除了能上传图片(即:头像字段),还携带了用户名、密码两个字段,很好的诠释了http带参数上传文件的情形。点击提交按钮后,浏览器会将文件(即头像文件)二进制数据和用户名、密码以post方式发送至服务器。这时我们可以通过抓包工具(如:fiddler)(或者浏览器自带的开发者工具F12)查看请求报文内容。

    HttpWebRequest携带参数上传图片等大文件

        通过抓包工具获取到携带参数上传文件请求报文体内容格式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    POST /PostUploadHandler.ashx HTTP/1.1
    Host: localhost:44187
    Connection: keep-alive
    Content-Length: 19839
    Cache-Control: max-age=0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    Origin: http://localhost:44187
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryNSF3vGLxKBlk5kcB
    Referer: http://localhost:44187/UploadDemo.aspx
    Accept-Encoding: gzip, deflate
    Accept-Language: zh-CN,zh;q=0.8
     
     
    ------WebKitFormBoundaryNSF3vGLxKBlk5kcB
    Content-Disposition: form-data; name="userName"
     
    admin
    ------WebKitFormBoundaryNSF3vGLxKBlk5kcB
    Content-Disposition: form-data; name="userPwd"
     
    123456
    ------WebKitFormBoundaryNSF3vGLxKBlk5kcB
    Content-Disposition: form-data; name="photo"; filename="1.png"
    Content-Type: image/png
     
    <!--这一行是文件二进制数据-->
    ------WebKitFormBoundaryNSF3vGLxKBlk5kcB--

        1、请求头中有一个Content-Type参数(默认值:application/x-www-form-urlencoded),其中multipart/form-data值表示向服务器发送二进制数据,boundary表示请求体的分界线,服务器就是依靠分界线分割请求体来读取数据,此参数值可自定义。

        2、请求体依靠boundary有规则的排列参数。每一行字符串后面包含一个换行符“ ”,有一个开始分界线(--boundary)和一个结束分界线(--boundary--),参数与参数之间通过--boundary分离,每一个参数的键(key)和值(value)之间包含一个空行即:“ "。

    二、完整版HttpWebRequest模拟上传文件请求报文内容封装

        通过上面介绍,我们已经清楚了解了http协议上传文件的POST请求报文内容格式,在.net中使用HttpWebRequest上传文件,我们只要按照此格式封装请求报文,即可实现携带参数上传功能了。

        为了方便扩展和维护,把所有请求参数(如上传地址url、携带参数、上传文件流等)封装到一个类中,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    /// <summary>
    /// 上传文件 - 请求参数类
    /// </summary>
    public class UploadParameterType
    {
        public UploadParameterType()
        {
            FileNameKey = "fileName";
            Encoding = Encoding.UTF8;
            PostParameters = new Dictionary<stringstring>();
        }
        /// <summary>
        /// 上传地址
        /// </summary>
        public string Url { getset; }
        /// <summary>
        /// 文件名称key
        /// </summary>
        public string FileNameKey { getset; }
        /// <summary>
        /// 文件名称value
        /// </summary>
        public string FileNameValue { getset; }
        /// <summary>
        /// 编码格式
        /// </summary>
        public Encoding Encoding { getset; }
        /// <summary>
        /// 上传文件的流
        /// </summary>
        public Stream UploadStream { getset; }
        /// <summary>
        /// 上传文件 携带的参数集合
        /// </summary>
        public IDictionary<stringstring> PostParameters { getset; } 
    }

        新建一个上传文件工具类(命名为:HttpUploadClient),在类中增加上传方法(命名为:Execute),如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /// <summary>
    /// Http上传文件类 - HttpWebRequest封装
    /// </summary>
    public class HttpUploadClient
    {
        /// <summary>
        /// 上传执行 方法
        /// </summary>
        /// <param name="parameter">上传文件请求参数</param>
        public static string Execute(UploadParameterType parameter)
        {
             
        }
        static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }
    }

        Post上传请求体参数是二进制格式的,我们只需要将参数根据以上报文体内容格式拼接好数据,存放在内存流里面,拼接完整后,将整个内存流转换成二进制格式写入到HttpWebRequest请求体中就行,下面我们来一步一步的拼接报文体内容。

        1、定义开始结束分界线boundary及拼接开始分界线:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public static string Execute(UploadParameterType parameter)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            // 1.分界线
            string boundary = string.Format("----{0}", DateTime.Now.Ticks.ToString("x")),       // 分界线可以自定义参数
                beginBoundary = string.Format("--{0} ", boundary),
                endBoundary = string.Format(" --{0}-- ", boundary);
            byte[] beginBoundaryBytes = parameter.Encoding.GetBytes(beginBoundary),
                endBoundaryBytes = parameter.Encoding.GetBytes(endBoundary);
            // 2.组装开始分界线数据体 到内存流中
            memoryStream.Write(beginBoundaryBytes, 0, beginBoundaryBytes.Length);
            // ……
        }
    }

        2、拼接附加携带参数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 3.组装 上传文件附加携带的参数 到内存流中
    if (parameter.PostParameters != null && parameter.PostParameters.Count > 0)
    {
        foreach (KeyValuePair<stringstring> keyValuePair in parameter.PostParameters)
        {
            string parameterHeaderTemplate = string.Format("Content-Disposition: form-data; name="{0}" {1} {2}", keyValuePair.Key, keyValuePair.Value, beginBoundary);
            byte[] parameterHeaderBytes = parameter.Encoding.GetBytes(parameterHeaderTemplate);
     
            memoryStream.Write(parameterHeaderBytes, 0, parameterHeaderBytes.Length);
        }
    }

        3、拼接上传文件体及结束分界线boundary(需要注意的是Content-Type的值是:application/octet-stream):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 4.组装文件头数据体 到内存流中
    string fileHeaderTemplate = string.Format("Content-Disposition: form-data; name="{0}"; filename="{1}" Content-Type: application/octet-stream ", parameter.FileNameKey, parameter.FileNameValue);
    byte[] fileHeaderBytes = parameter.Encoding.GetBytes(fileHeaderTemplate);
    memoryStream.Write(fileHeaderBytes, 0, fileHeaderBytes.Length);
    // 5.组装文件流 到内存流中
    byte[] buffer = new byte[1024 * 1024 * 1];
    int size = parameter.UploadStream.Read(buffer, 0, buffer.Length);
    while (size > 0)
    {
        memoryStream.Write(buffer, 0, size);
        size = parameter.UploadStream.Read(buffer, 0, buffer.Length);
    }
    // 6.组装结束分界线数据体 到内存流中
    memoryStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);

        4、通过以上步骤,上传文件请求体内容数据已经拼接完成,接下来就是对HttpWebRequest对象的属性设置(如:请求地址Url,请求方法Method,Content-Type等),把整个上传文件请求体内存流写入到HttpWebRequest对象的请求体中,然后发起上传请求。如下源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 7.获取二进制数据
    byte[] postBytes = memoryStream.ToArray();
    // 8.HttpWebRequest 组装
    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(parameter.Url, UriKind.RelativeOrAbsolute));
    webRequest.Method = "POST";
    webRequest.Timeout = 10000;
    webRequest.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
    webRequest.ContentLength = postBytes.Length;
    if (Regex.IsMatch(parameter.Url, "^https://"))
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
        ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult;
    }
    // 9.写入上传请求数据
    using (Stream requestStream = webRequest.GetRequestStream())
    {
        requestStream.Write(postBytes, 0, postBytes.Length);
        requestStream.Close();
    }
    // 10.获取响应
    using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
    {
        using (StreamReader reader = new StreamReader(webResponse.GetResponseStream(), parameter.Encoding))
        {
            string body = reader.ReadToEnd();
            reader.Close();
            return body;
        }
    }

        完整版HttpWebRequest模拟上传文件代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    /// <summary>
    /// Http上传文件类 - HttpWebRequest封装
    /// </summary>
    public class HttpUploadClient
    {
        /// <summary>
        /// 上传执行 方法
        /// </summary>
        /// <param name="parameter">上传文件请求参数</param>
        public static string Execute(UploadParameterType parameter)
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                // 1.分界线
                string boundary = string.Format("----{0}", DateTime.Now.Ticks.ToString("x")),       // 分界线可以自定义参数
                    beginBoundary = string.Format("--{0} ", boundary),
                    endBoundary = string.Format(" --{0}-- ", boundary);
                byte[] beginBoundaryBytes = parameter.Encoding.GetBytes(beginBoundary),
                    endBoundaryBytes = parameter.Encoding.GetBytes(endBoundary);
                // 2.组装开始分界线数据体 到内存流中
                memoryStream.Write(beginBoundaryBytes, 0, beginBoundaryBytes.Length);
                // 3.组装 上传文件附加携带的参数 到内存流中
                if (parameter.PostParameters != null && parameter.PostParameters.Count > 0)
                {
                    foreach (KeyValuePair<stringstring> keyValuePair in parameter.PostParameters)
                    {
                        string parameterHeaderTemplate = string.Format("Content-Disposition: form-data; name="{0}" {1} {2}", keyValuePair.Key, keyValuePair.Value, beginBoundary);
                        byte[] parameterHeaderBytes = parameter.Encoding.GetBytes(parameterHeaderTemplate);
     
                        memoryStream.Write(parameterHeaderBytes, 0, parameterHeaderBytes.Length);
                    }
                }
                // 4.组装文件头数据体 到内存流中
                string fileHeaderTemplate = string.Format("Content-Disposition: form-data; name="{0}"; filename="{1}" Content-Type: application/octet-stream ", parameter.FileNameKey, parameter.FileNameValue);
                byte[] fileHeaderBytes = parameter.Encoding.GetBytes(fileHeaderTemplate);
                memoryStream.Write(fileHeaderBytes, 0, fileHeaderBytes.Length);
                // 5.组装文件流 到内存流中
                byte[] buffer = new byte[1024 * 1024 * 1];
                int size = parameter.UploadStream.Read(buffer, 0, buffer.Length);
                while (size > 0)
                {
                    memoryStream.Write(buffer, 0, size);
                    size = parameter.UploadStream.Read(buffer, 0, buffer.Length);
                }
                // 6.组装结束分界线数据体 到内存流中
                memoryStream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);
                // 7.获取二进制数据
                byte[] postBytes = memoryStream.ToArray();
                // 8.HttpWebRequest 组装
                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(new Uri(parameter.Url, UriKind.RelativeOrAbsolute));
                webRequest.Method = "POST";
                webRequest.Timeout = 10000;
                webRequest.ContentType = string.Format("multipart/form-data; boundary={0}", boundary);
                webRequest.ContentLength = postBytes.Length;
                if (Regex.IsMatch(parameter.Url, "^https://"))
                {
                    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
                    ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult;
                }
                // 9.写入上传请求数据
                using (Stream requestStream = webRequest.GetRequestStream())
                {
                    requestStream.Write(postBytes, 0, postBytes.Length);
                    requestStream.Close();
                }
                // 10.获取响应
                using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
                {
                    using (StreamReader reader = new StreamReader(webResponse.GetResponseStream(), parameter.Encoding))
                    {
                        string body = reader.ReadToEnd();
                        reader.Close();
                        return body;
                    }
                }
            }
        }
        static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }
    }

        为了验证封装是否正确,可以写一个控制台应用程序来模拟Http协议上传文件(以图片为例),结果如图:

    HttpWebRequest携带参数上传图片等大文件实例源码

    三、asp.net(c#)使用HttpWebRequest携带请求参数模拟上传文件封装源码下载

        HttpWebRequest模拟上传文件封装源码

    扫一扫获取百度网盘超级vip账号

  • 相关阅读:
    WebDriverException: Message: 'geckodriver' executable needs to be in PATH.
    Django安装与使用
    初识Django
    python学习之xlrd的使用
    python 学习笔记
    根据当前日期生成一个唯一标识的名称
    用Python生成组织机构代码,附源码
    IO流基础
    多线程
    日期时间类
  • 原文地址:https://www.cnblogs.com/GodX/p/5604944.html
Copyright © 2011-2022 走看看