码迷,mamicode.com
首页 > Windows程序 > 详细

[C#]通过Http报文上传文件

时间:2015-09-21 21:27:25      阅读:840      评论:0      收藏:0      [点我收藏+]

标签:

前言

这段时间做C#客户端项目时,在网上找到一个用Http请求实现文件上传的方法,实测有效。

在讲解如何实现Http上传文件之前,不妨先了解一下Http上传文件报文是什么样的。相信看了报文结构,有利于了解代码的实现过程。

下图是我自己的程序上传文件时时,用wireshark抓取的包内容。

可以看到,这个Http报文结构其实并不复杂。

需要注意的是,这个报文中有一个boundary,这是一个识别文件流的边界,用来标识文件开始和结尾的位置。

技术分享

接下来,我将一步步说明如何封装Http上传文件报文。

C#中用Http请求实现文件上传

封装Http上传文件报文大概有五个步骤。

1.     初始化HttpWebRequest(需要引用System.Net)

2.     封装Http cookie

3.     生成时间戳,并封装Http报文头

4.     封装请求内容,并将封装好的请求报文用Stream类写入流(需要引用System.IO)

5.     接收应答报文

接下来,我们一步一步来讲解如何在C#中完成Http请求报文的封装。


初始化HttpWebRequest

使用请求地址作为参数,初始化一个HttpWebRequest实例。

// 初始化HttpWebRequest
HttpWebRequest httpRequest = (HttpWebRequest)HttpWebRequest.Create(strRequestUri);

 

封装Http cookie

首先,简单说明一下Cookie。有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。本文为了示例,就以比较简单的形式展现。

在C#中,使用Cookie类来封装cookie,以键值对的形式保存。

然后,将这个封装好的cookie添加到CookieContainer容器中,最后填入HttpWebRequest。既然是容器,顾名思义,可以添加多个cookie。

// 封装Cookie
Uri uri = new Uri(strRequestUri);
Cookie cookie = new Cookie("Name", strCookie); // 设置key、value形式的Cookie
CookieContainer cookies = new CookieContainer();
cookies.Add(uri, cookie);
httpRequest.CookieContainer = cookies;

 

封装Http Header

Http上传文件报文,需要设置一个边界,这个边界可以是任意的。

这里,我们不妨用一个时间戳来作为边界。记得要将这个边界在报文头标识出来,这样服务器收到这个请求后就能识别边界了。

以下是一个简单范例:

// 生成时间戳
string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");
byte[] boundaryBytes = Encoding.ASCII.GetBytes(string.Format("\r\n--{0}--\r\n", strBoundary));
 
// 填报文类型
httpRequest.Method = "Post";
httpRequest.Timeout = 1000 * 120;
httpRequest.ContentType = "multipart/form-data; boundary=" + strBoundary;

 

写文件流

说是写文件流,其实这里我用的方法是将整个报文的头部、文件内容、尾部统统以流的形式发出去。

首先是封装报文头

封装Http报文时有几个小细节

(1)\"是用转义符确保输出双引号

(2)由于Http报文的格式要求,请求头部的结束部分需要有回车符和换行符

(3)在发送文件流内容时,可能会出现文件太大,而超过报文允许的最大字节数这种情况。可以采用分包发送的方式将文件传输。 

// 生成时间戳
string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");
byte[] boundaryBytes = Encoding.ASCII.GetBytes(string.Format("\r\n--{0}--\r\n", strBoundary));

// 填报文类型
httpRequest.Method = "Post";
httpRequest.Timeout = 1000 * 120;
httpRequest.ContentType = "multipart/form-data; boundary=" + strBoundary;

// 封装HTTP报文头的流
StringBuilder sb = new StringBuilder();
sb.Append("--");
sb.Append(strBoundary);
sb.Append(Environment.NewLine);
sb.Append("Content-Disposition: form-data; name=\"");
sb.Append("file");
sb.Append("\"; filename=\"");
sb.Append(filename);
sb.Append("\"");
sb.Append(Environment.NewLine);
sb.Append("Content-Type: ");
sb.Append("multipart/form-data;");
sb.Append(Environment.NewLine);
sb.Append(Environment.NewLine);
byte[] postHeaderBytes = Encoding.UTF8.GetBytes(sb.ToString());

// 计算报文长度
FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
long length = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length;
httpRequest.ContentLength = length;
fileStream.Close();

// 将报文头写入流
Stream requestStream = httpRequest.GetRequestStream();
requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);

// 将上传文件内容写入流
byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
    requestStream.Write(buffer, 0, bytesRead);

// 将报文尾部写入流
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);

// 关闭流
requestStream.Close();
fileStream.Close();

 

如果按照以上代码封装Http头部,打印出来的内容大概如下:

------------8d2c2be6186514f
Content-Disposition: form-data; name="file"; filename="Desert.jpg"
Content-Type: multipart/form-data;
 
}

 

获得应答报文

通过流将请求报文发出去后,可以通过HttpWebRequest的GetResponse()方法来获取HttpWebResponse。

// 获得应答报文
HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
Stream responseStream = httpResponse.GetResponseStream();
StreamReader reader = new StreamReader(responseStream, System.Text.Encoding.UTF8);
string strResponse = reader.ReadToEnd();
reader.Close();
responseStream.Close();

 

附上完整方法,供大家参考。

C#中利用Http上传文件的方式应该有很多,我找到的方法可能不是最好的,欢迎大家不吝赐教。

技术分享
private string UploadFile(string strRequestUri, string strCookie, string filename)
{
    // 初始化HttpWebRequest
    HttpWebRequest httpRequest = (HttpWebRequest)HttpWebRequest.Create(strRequestUri);

    // 封装Cookie
    Uri uri = new Uri(strRequestUri);
    Cookie cookie = new Cookie("Name", strCookie); // 设置key、value形式的Cookie
    CookieContainer cookies = new CookieContainer();
    cookies.Add(uri, cookie);
    httpRequest.CookieContainer = cookies;

// 生成时间戳
string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");
byte[] boundaryBytes = Encoding.ASCII.GetBytes(string.Format("\r\n--{0}--\r\n", strBoundary));

// 填报文类型
httpRequest.Method = "Post";
httpRequest.Timeout = 1000 * 120;
httpRequest.ContentType = "multipart/form-data; boundary=" + strBoundary;

// 封装HTTP报文头的流
StringBuilder sb = new StringBuilder();
sb.Append("--");
sb.Append(strBoundary);
sb.Append(Environment.NewLine);
sb.Append("Content-Disposition: form-data; name=\"");
sb.Append("file");
sb.Append("\"; filename=\"");
sb.Append(filename);
sb.Append("\"");
sb.Append(Environment.NewLine);
sb.Append("Content-Type: ");
sb.Append("multipart/form-data;");
sb.Append(Environment.NewLine);
sb.Append(Environment.NewLine);
byte[] postHeaderBytes = Encoding.UTF8.GetBytes(sb.ToString());

// 计算报文长度
FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read);
long length = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length;
httpRequest.ContentLength = length;
fileStream.Close();

// 将报文头写入流
Stream requestStream = httpRequest.GetRequestStream();
requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);

// 将上传文件内容写入流
byte[] buffer = new Byte[checked((uint)Math.Min(4096, (int)fileStream.Length))];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
    requestStream.Write(buffer, 0, bytesRead);

// 将报文尾部写入流
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);

// 关闭流
requestStream.Close();
fileStream.Close();

    // 获得应答报文
    HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
    Stream responseStream = httpResponse.GetResponseStream();
    StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
    string strResponse = reader.ReadToEnd();
    reader.Close();
    responseStream.Close();
    return strResponse;
}
View Code

[C#]通过Http报文上传文件

标签:

原文地址:http://www.cnblogs.com/jingmoxukong/p/4827191.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!