【轉】asp.net(c#)使用HttpWebRequest附加攜帶請求參數以post方式模擬上傳大文件(以圖片爲例)到Web服務器端

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

目錄:web

一、Http協議上傳文件(以圖片爲例)請求報文體內容格式c#

二、完整版HttpWebRequest模擬上傳文件請求報文內容封裝瀏覽器

三、asp.net(c#)使用HttpWebRequest攜帶請求參數模擬上傳文件封裝源碼下載服務器

1、Http協議上傳文件(以圖片爲例)請求報文體內容格式

    首先,咱們來看下經過瀏覽器上傳文件的請求報文內容格式,這裏以本人本身寫的實例爲例,以下圖。除了能上傳圖片(即:頭像字段),還攜帶了用戶名、密碼兩個字段,很好的詮釋了http帶參數上傳文件的情形。點擊提交按鈕後,瀏覽器會將文件(即頭像文件)二進制數據和用戶名、密碼以post方式發送至服務器。這時咱們能夠經過抓包工具(如:fiddler)(或者瀏覽器自帶的開發者工具F12)查看請求報文內容。app

HttpWebRequest攜帶參數上傳圖片等大文件

    經過抓包工具獲取到攜帶參數上傳文件請求報文體內容格式以下:asp.net

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--

    一、請求頭中有一個Content-Type參數(默認值:application/x-www-form-urlencoded),其中multipart/form-data值表示向服務器發送二進制數據,boundary表示請求體的分界線,服務器就是依靠分界線分割請求體來讀取數據,此參數值可自定義。工具

    二、請求體依靠boundary有規則的排列參數。每一行字符串後面包含一個換行符「\r\n」,有一個開始分界線(--boundary)和一個結束分界線(--boundary--),參數與參數之間經過--boundary分離,每個參數的鍵(key)和值(value)之間包含一個空行即:「\r\n"。post

 

2、完整版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< string string >();
     }
     /// <summary>
     /// 上傳地址
     /// </summary>
     public  string  Url {  get set ; }
     /// <summary>
     /// 文件名稱key
     /// </summary>
     public  string  FileNameKey {  get set ; }
     /// <summary>
     /// 文件名稱value
     /// </summary>
     public  string  FileNameValue {  get set ; }
     /// <summary>
     /// 編碼格式
     /// </summary>
     public  Encoding Encoding {  get set ; }
     /// <summary>
     /// 上傳文件的流
     /// </summary>
     public  Stream UploadStream {  get set ; }
     /// <summary>
     /// 上傳文件 攜帶的參數集合
     /// </summary>
     public  IDictionary< string string > PostParameters {  get set ; } 
}

    新建一個上傳文件工具類(命名爲: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請求體中就行,下面咱們來一步一步的拼接報文體內容。

    一、定義開始結束分界線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}\r\n" , boundary),
             endBoundary =  string .Format( "\r\n--{0}--\r\n" , boundary);
         byte [] beginBoundaryBytes = parameter.Encoding.GetBytes(beginBoundary),
             endBoundaryBytes = parameter.Encoding.GetBytes(endBoundary);
         // 2.組裝開始分界線數據體 到內存流中
         memoryStream.Write(beginBoundaryBytes, 0, beginBoundaryBytes.Length);
         // ……
     }
}

    二、拼接附加攜帶參數:

1
2
3
4
5
6
7
8
9
10
11
// 3.組裝 上傳文件附加攜帶的參數 到內存流中
if  (parameter.PostParameters !=  null  && parameter.PostParameters.Count > 0)
{
     foreach  (KeyValuePair< string string > keyValuePair  in  parameter.PostParameters)
     {
         string  parameterHeaderTemplate =  string .Format( "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}\r\n{2}" , keyValuePair.Key, keyValuePair.Value, beginBoundary);
         byte [] parameterHeaderBytes = parameter.Encoding.GetBytes(parameterHeaderTemplate);
 
         memoryStream.Write(parameterHeaderBytes, 0, parameterHeaderBytes.Length);
     }
}

    三、拼接上傳文件體及結束分界線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}\"\r\nContent-Type: application/octet-stream\r\n\r\n" , 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);

    四、經過以上步驟,上傳文件請求體內容數據已經拼接完成,接下來就是對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}\r\n" , boundary),
                 endBoundary =  string .Format( "\r\n--{0}--\r\n" , 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< string string > keyValuePair  in  parameter.PostParameters)
                 {
                     string  parameterHeaderTemplate =  string .Format( "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}\r\n{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}\"\r\nContent-Type: application/octet-stream\r\n\r\n" , 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攜帶參數上傳圖片等大文件實例源碼

3、asp.net(c#)使用HttpWebRequest攜帶請求參數模擬上傳文件封裝源碼下載

    HttpWebRequest模擬上傳文件封裝源碼

 

掃一掃獲取百度網盤超級vip帳號

相關文章
相關標籤/搜索