原文地址:http://docode.top/Article/Detail/10002 目錄: 1、Http協議上傳文件(以圖片為例)請求報文體內容格式 2、完整版HttpWebRequest模擬上傳文件請求報文內容封裝 3、asp.net(c#)使用HttpWebRequest攜帶請求參數模擬上傳 ...
原文地址:http://docode.top/Article/Detail/10002
目錄:
2、完整版HttpWebRequest模擬上傳文件請求報文內容封裝
3、asp.net(c#)使用HttpWebRequest攜帶請求參數模擬上傳文件封裝源碼下載
一、Http協議上傳文件(以圖片為例)請求報文體內容格式
首先,我們來看下通過瀏覽器上傳文件的請求報文內容格式,這裡以本人自己寫的實例為例,如下圖。除了能上傳圖片(即:頭像欄位),還攜帶了用戶名、密碼兩個欄位,很好的詮釋了http帶參數上傳文件的情形。點擊提交按鈕後,瀏覽器會將文件(即頭像文件)二進位數據和用戶名、密碼以post方式發送至伺服器。這時我們可以通過抓包工具(如:fiddler)(或者瀏覽器自帶的開發者工具F12)查看請求報文內容。
通過抓包工具獲取到攜帶參數上傳文件請求報文體內容格式如下:
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有規則的排列參數。每一行字元串後麵包含一個換行符“\r\n”,有一個開始分界線(--boundary)和一個結束分界線(--boundary--),參數與參數之間通過--boundary分離,每一個參數的鍵(key)和值(value)之間包含一個空行即:“\r\n"。
二、完整版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請求體中就行,下麵我們來一步一步的拼接報文體內容。
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}\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);
// ……
}
}
|
2、拼接附加攜帶參數:
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);
}
}
|
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}\"\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);
|
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模擬上傳文件代碼如下: