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

前言

这段时间做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;
}

时间: 2024-12-20 17:43:12

[C#]通过Http报文上传文件的相关文章

JAVA模拟HTTP post请求上传文件

在开发中,我们使用的比较多的HTTP请求方式基本上就是GET.POST.其中GET用于从服务器获取数据,POST主要用于向服务器提交一些表单数据,例如文件上传等.而我们在使用HTTP请求时中遇到的比较麻烦的事情就是构造文件上传的HTTP报文格式,这个格式虽说也比较简单,但也比较容易出错.今天我们就一起来学习HTTP POST的报文格式以及通过Java来模拟文件上传的请求. 首先我们来看一个POST的报文请求,然后我们再来详细的分析它. POST报文格式 [plain] view plain co

node 上传文件 http client to post file

node做http client 发送post数据是很容易的事情,但要上传文件就不是太容易了主要是因为上传文件的报文和普通post是不太一样的 要了解http post可以看下这个 https://imququ.com/post/four-ways-to-post-data-in-http.html npm上封装好的第三方库很多 比如request,我们来看下自己实现需要怎么做 首先要声称个随机串,这个是用来做分段的标记 var boundaryKey = Math.random().toStr

关于HttpWebRequest上传文件

我们web 操作离不开 http请求响应 HttpWebRequest上传文件也是一样的道理 下面码一些代码: private void UploadFile(string strRequestUri, string strCookie, string filename) { // 初始化HttpWebRequest HttpWebRequest httpRequest = (HttpWebRequest)HttpWebRequest.Create(strRequestUri); // 封装Co

Servlet 实现上传文件以及同时,写入xml格式文件和上传

package com.isoftstone.eply.servlet; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io

使用HTML的表单form上传文件,需要考虑的几个问题

应用系统中经常需要有文件上传功能,一般的做法都是使用HTML的<form>和<input type="file">,或者使用第三方文件上传组件,如swfupload和uploadify.我们都知道如果向服务器提交数据,一般来说都是使用POST请求,请求数据会放在请求体中,以key1=value1&key2=value2的形式.这样的报文,服务器是很容易解析的.如果是上传文件,通过httpwatch抓包工具,我们可以发现:文件的内容也是放在post请求体中

SpringMVC+jquery.uploadify 上传文件

前言 以前用Asp.net MVC+uploadify上传文件,最近学习SpringMVC,所以就用SpringMVC+uploadify做个上传文件的demo. 刚开始用form表单的方式提交,在Controller Action中用@RequestParam MultipartFile file就能拿到上传文件信息.后我直接使用uploadify的方式上传,接口没有做任何调整,上传的过程中报http400, 客户端的请求不符合接口的要求,表单post提交时报文参数是以Form Data方式,

Ajax使用formdata异步上传文件,报错the request was rejected because no multipart boundary was found

基于jQuery的Ajaxs使用FormData上传文件要注意两个参数的设定 processData设为false 把processData设为false,让jquery不要对formData做处理,如果processData不设置为false,jquery会把formData转换为字符串. contentType设为false http发送multipart/form-data请求报文示例 POST /api/feed/ HTTP/1.1 Accept-Encoding: gzip Conte

接口测试-Http状态码-postman上传文件

转自:https://www.cnblogs.com/jiadan/articles/8546015.html 一. 接口   接口:什么是接口呢?接口一般来说有两种,一种是程序内部的接口,一种是系统对外的接口.   系统对外的接口:比如你要从别的网站或服务器上获取资源或信息,别人肯定不会把数据库共享给你,他只能给你提供一个他们写好的方法来获取数据,你引用他提供的接口就能使用他写好的方法,从而达到数据共享的目的,比如说咱们用的app.网址这些它在进行数据处理的时候都是通过接口来进行调用的.  

文件上传控件-如何上传文件-文件夹断点续传

最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表格数据.上传影音文件等.如果文件体积比较大,或者网络条件不好时,上传的时间会比较长(要传输更多的报文,丢包重传的概率也更大),用户不能刷新页面,只能耐心等待请求完成. 下面从文件上传方式入手,整理大文件上传的思路,并给出了相关实例代码,由于PHP内置了比较方便的文件拆分和拼接方法,因此服务端代码使用