关于webclient提交数据时http头的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于webclient提交数据时http头的问题相关的知识,希望对你有一定的参考价值。

http头中有个字段"Connection",我想让这个字段的值为"Keep-Alive"
但用WebClient.Headers.Add(HttpRequestHeader.Connection, "Keep-Alive")设置后,提交数据时就会出错,出错提示为:

“System.Net.WebException: 在 WebClient 请求期间发生异常。 ---> System.ArgumentException: Keep-Alive 和 Close 不能使用此属性设置。”

这个提示的意思大概就是WebClient的Connection不能设置,那还有别的办法可以让Connection变成Keep-Alive么?或者可以用什么替代WebClient?我只用WebClient来POST数据,可以改用什么作为替代而代码变动不大?

这个问题困扰我一下午了,希望大家帮忙解决,谢谢!

参考技术A 怕是 WebClient 根本就不支持 Keep-Alive 功能, 你就算绞尽脑汁终于让WebClient发出这个Header去了,也根本没有预料的效果。

刚检查了一下,WebClient使用的HttpWebRequest,而HttpWebRequest不支持Connection这个属性。你搜一下是否有第3放的WebClient替代品支持该属性的。没有的话恐怕得手动用TcpClient手动写了。
参考技术B 文档说HttpRequestHeader.Connection 是不能设置的,
你试试

WebClient.Headers.Add(HttpRequestHeader.KeepAlive,"TRUE")本回答被提问者采纳
参考技术C 可以用HttpWebRequest来代替WebClient 里面好像有个布尔属性是可以设置KeepAlive的 参考技术D 我有个提议不知道能不能奏效,我放假在家没法写代码试验哈。
大致是这样:你可以写一个函数,里面用来设置是keep-alive还是close,当你要post的时候把这个函数打包发出去,接收到这个函数时进行判断。
我只是这么想,具体实现的话需要调试哈。我也很想知道这个答案,可以慢慢研究

[C#]使用WebClient上传文件并同时Post表单数据字段到服务端

转自:http://www.97world.com/archives/2963

   之前遇到一个问题,就是使用WebClient上传文件的同时,还要Post表单数据字段,一开始以为WebClient可以直接做到,结果发现如果先Post表单字段,就只能获取到字段及其值,如果先上传文件,也只能获取到上传文件的内容。测试了不少时间才发现WebClient不能这么使用。

    Google到相关的解决思路和类,因为发现网上的一些文章不是介绍得太简单就是太复杂,所以这里简单整理一下,既能帮助自己巩固知识,也希望能够帮到大家!如果大家有什么不明白,可以直接留言问我。

    关于WebClient上传文件并同时Post表单数据的实现原理,大家可以参考这篇文章http://www.cnblogs.com/goody9807/archive/2007/06/06/773735.html,介绍得非常详细,但是类和实例有些模糊,所以类和实例可以直接参考本文。

HttpRequestClient类Code:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Net;
 
namespace Common.Helper
{
  /// <summary>
  /// description:http post请求客户端
  /// last-modified-date:2012-02-28
  /// </summary>
  public class HttpRequestClient
  {
    #region //字段
    private ArrayList bytesArray;
    private Encoding encoding = Encoding.UTF8;
    private string boundary = String.Empty;
    #endregion
 
    #region //构造方法
    public HttpRequestClient()
    {
      bytesArray = new ArrayList();
      string flag = DateTime.Now.Ticks.ToString("x");
      boundary = "---------------------------" + flag;
    }
    #endregion
 
    #region //方法
    /// <summary>
    /// 合并请求数据
    /// </summary>
    /// <returns></returns>
    private byte[] MergeContent()
    {
      int length = 0;
      int readLength = 0;
      string endBoundary = "--" + boundary + "--\\r\\n";
      byte[] endBoundaryBytes = encoding.GetBytes(endBoundary);
 
      bytesArray.Add(endBoundaryBytes);
 
      foreach (byte[] b in bytesArray)
      {
        length += b.Length;
      }
 
      byte[] bytes = new byte[length];
 
      foreach (byte[] b in bytesArray)
      {
        b.CopyTo(bytes, readLength);
        readLength += b.Length;
      }
 
      return bytes;
    }
 
    /// <summary>
    /// 上传
    /// </summary>
    /// <param name="requestUrl">请求url</param>
    /// <param name="responseText">响应</param>
    /// <returns></returns>
    public bool Upload(String requestUrl, out String responseText)
    {
      WebClient webClient = new WebClient();
      webClient.Headers.Add("Content-Type""multipart/form-data; boundary=" + boundary);
 
      byte[] responseBytes;
      byte[] bytes = MergeContent();
 
      try
      {
        responseBytes = webClient.UploadData(requestUrl, bytes);
        responseText = System.Text.Encoding.UTF8.GetString(responseBytes);
        return true;
      }
      catch (WebException ex)
      {
        Stream responseStream = ex.Response.GetResponseStream();
        responseBytes = new byte[ex.Response.ContentLength];
        responseStream.Read(responseBytes, 0, responseBytes.Length);
      }
      responseText = System.Text.Encoding.UTF8.GetString(responseBytes);
      return false;
    }
 
    /// <summary>
    /// 设置表单数据字段
    /// </summary>
    /// <param name="fieldName">字段名</param>
    /// <param name="fieldValue">字段值</param>
    /// <returns></returns>
    public void SetFieldValue(String fieldName, String fieldValue)
    {
      string httpRow = "--" + boundary + "\\r\\nContent-Disposition: form-data; name=\\"{0}\\"\\r\\n\\r\\n{1}\\r\\n";
      string httpRowData = String.Format(httpRow, fieldName, fieldValue);
 
      bytesArray.Add(encoding.GetBytes(httpRowData));
    }
 
    /// <summary>
    /// 设置表单文件数据
    /// </summary>
    /// <param name="fieldName">字段名</param>
    /// <param name="filename">字段值</param>
    /// <param name="contentType">内容内型</param>
    /// <param name="fileBytes">文件字节流</param>
    /// <returns></returns>
    public void SetFieldValue(String fieldName, String filename, String contentType, Byte[] fileBytes)
    {
      string end = "\\r\\n";
      string httpRow = "--" + boundary + "\\r\\nContent-Disposition: form-data; name=\\"{0}\\"; filename=\\"{1}\\"\\r\\nContent-Type: {2}\\r\\n\\r\\n";
      string httpRowData = String.Format(httpRow, fieldName, filename, contentType);
 
      byte[] headerBytes = encoding.GetBytes(httpRowData);
      byte[] endBytes = encoding.GetBytes(end);
      byte[] fileDataBytes = new byte[headerBytes.Length + fileBytes.Length + endBytes.Length];
 
      headerBytes.CopyTo(fileDataBytes, 0);
      fileBytes.CopyTo(fileDataBytes, headerBytes.Length);
      endBytes.CopyTo(fileDataBytes, headerBytes.Length + fileBytes.Length);
 
      bytesArray.Add(fileDataBytes);
    }
    #endregion
  }
}
客户端实例代码:
01
02
03
04
05
06
07
08
09
10
string fileFullName=@"c:\\test.txt",filedValue="hello_world",responseText = "";
FileStream fs = new FileStream(fileFullName, System.IO.FileMode.Open, System.IO.FileAccess.Read);
byte[] fileBytes = new byte[fs.Length];
fs.Read(fileBytes, 0, fileBytes.Length);
fs.Close(); fs.Dispose();
 
HttpRequestClient httpRequestClient = new HttpRequestClient();
httpRequestClient.SetFieldValue("key", filedValue);
httpRequestClient.SetFieldValue("uploadfile", Path.GetFileName(fileFullName), "application/octet-stream", fileBytes);
httpRequestClient.Upload(NormalBotConfig.Instance.GetUploadFileUrl(), out responseText);
服务端实例代码:
1
2
3
4
5
6
7
8
if (HttpContext.Current.Request.Files.AllKeys.Length > 0)
{
  string filePath = Path.Combine(HttpContext.Current.Server.MapPath("~/"), "UploadFile", DateTime.Now.Year.ToString(), DateTime.Now.Month.ToString(), DateTime.Now.Day.ToString());
  if (!Directory.Exists(filePath)) Directory.CreateDirectory(filePath);
  //这里我直接用索引来获取第一个文件,如果上传了多个文件,可以通过遍历HttpContext.Current.Request.Files.AllKeys取“key值”,再通过HttpContext.Current.Request.Files[“key值”]获取文件
  HttpContext.Current.Request.Files[0].SaveAs(Path.Combine(filePath, HttpContext.Current.Request.Files[0].FileName));
  string filedValue = HttpContext.Current.Request.Form["key"];
}

 

使用WebClient或HttpWebRequest模拟上传文件和数据

假如某网站有个表单,例如(url: http://localhost/login.aspx):
帐号  
密码  

我们需要在程序中提交数据到这个表单,对于这种表单,我们可以使用 WebClient.UploadData 方法来实现,将所要上传的数据拼成字符即可,程序很简单:

string uriString = "http://localhost/login.aspx";
// 创建一个新的 WebClient 实例.
WebClient myWebClient = new WebClient();
string postData = "Username=admin&Password=admin";
// 注意这种拼字符串的ContentType
myWebClient.Headers.Add("Content-Type","application/x-www-form-urlencoded");
// 转化成二进制数组
byte[] byteArray = Encoding.ASCII.GetBytes(postData);
// 上传数据,并获取返回的二进制数据.
byte[] responseArray = myWebClient.UploadData(uriString,"POST",byteArray);


对于文件上传类的表单,例如(url: http://localhost/uploadFile.aspx):
文件  

对于这种表单,我们可以使用
String uriString = "http://localhost/uploadFile.aspx";

// 创建一个新的 WebClient 实例.
WebClient myWebClient = new WebClient();

string fileName = @"C:\\upload.txt";

// 直接上传,并获取返回的二进制数据.
byte[] responseArray = myWebClient.UploadFile(uriString,"POST",fileName);


还有一种表单,不仅有文字,还有文件,例如(url: http://localhost/uploadData.aspx):
文件名  
文件  

对于这种表单,似乎前面的两种方法都不能适用,对于第一种方法,不能直接拼字符串,对于第二种,我们只能传文件,重新回到第一个方法,注意参数:
public byte[] UploadData(
   string address,
   string method,
   byte[] data
);
在第一个例子中,是通过拼字符串来得到byte[] data参数值的,对于这种表单显然不行,反过来想想,对于uploadData.aspx这样的程序来说,直接通过网页提交数据,后台所获取到的流是什么样的呢?(在我以前的一篇blog中,曾分析过这个问题:asp无组件上传进度条解决方案),最终的数据如下:

-----------------------------7d429871607fe
Content-Disposition: form-data; name="file1"; filename="G:\\homepage.txt"
Content-Type: text/plain
宝玉:http://www.webuc.net
-----------------------------7d429871607fe
Content-Disposition: form-data; name="filename"
default filename
-----------------------------7d429871607fe--


所以只要拼一个这样的byte[] data数据Post过去,就可以达到同样的效果了。但是一定要注意,对于这种带有文件上传的,其ContentType是不一样的,例如上面的这种,其ContentType为"multipart/form-data; boundary=---------------------------7d429871607fe"。有了ContentType,我们就可以知道boundary(就是上面的"---------------------------7d429871607fe"),知道boundary了我们就可以构造出我们所需要的byte[] data了,最后,不要忘记,把我们构造的ContentType传到WebClient中(例如:webClient.Headers.Add("Content-Type", ContentType);)这样,就可以通过WebClient.UploadData 方法上载文件数据了。

具体代码如下:
生成二进制数据类的封装

using System;
using System.Web;
using System.IO;
using System.Net;
using System.Text;
using System.Collections;

namespace UploadData.Common
{
    /**//// <summary>
    /// 创建WebClient.UploadData方法所需二进制数组
    /// </summary>
    public class CreateBytes
    {
        Encoding encoding = Encoding.UTF8;

        /**//// <summary>
        /// 拼接所有的二进制数组为一个数组
        /// </summary>
        /// <param name="byteArrays">数组</param>
        /// <returns></returns>
        /// <remarks>加上结束边界</remarks>
        public byte[] JoinBytes(ArrayList byteArrays)
        {
            int length = 0;
            int readLength = 0;

            // 加上结束边界
            string endBoundary = Boundary + "--\\r\\n"; //结束边界
            byte[] endBoundaryBytes = encoding.GetBytes(endBoundary);
            byteArrays.Add(endBoundaryBytes);

            foreach(byte[] b in byteArrays)
            {
                length += b.Length;
            }
            byte[] bytes = new byte[length];

            // 遍历复制
            //
            foreach(byte[] b in byteArrays)
            {
                b.CopyTo(bytes, readLength);
                readLength += b.Length;
            }

            return bytes;
        }

        public bool UploadData(string uploadUrl, byte[] bytes, out byte[] responseBytes)
        {
            WebClient webClient = new WebClient();
            webClient.Headers.Add("Content-Type", ContentType);

            try
            {
                responseBytes = webClient.UploadData(uploadUrl, bytes);
                return true;
            }
            catch (WebException ex)
            {
                Stream resp = ex.Response.GetResponseStream();
                responseBytes = new byte[ex.Response.ContentLength];
                resp.Read(responseBytes, 0, responseBytes.Length);                
            }
            return false; 
        }



        /**//// <summary>
        /// 获取普通表单区域二进制数组
        /// </summary>
        /// <param name="fieldName">表单名</param>
        /// <param name="fieldValue">表单值</param>
        /// <returns></returns>
        /// <remarks>
        /// -----------------------------7d52ee27210a3c\\r\\nContent-Disposition: form-data; name=\\"表单名\\"\\r\\n\\r\\n表单值\\r\\n
        /// </remarks>
        public byte[] CreateFieldData(string fieldName, string fieldValue)
        {
            string textTemplate = Boundary + "\\r\\nContent-Disposition: form-data; name=\\"{0}\\"\\r\\n\\r\\n{1}\\r\\n";
            string text = String.Format(textTemplate, fieldName, fieldValue);
            byte[] bytes = encoding.GetBytes(text);
            return bytes;
        }

        
        /**/使用angularjs的$http.post异步提交数据时,服务器接收不了的问题

关于http请求头的一些事

关于分布式事务两阶段提交协议三阶提交协议

[C#]使用WebClient上传文件并同时Post表单数据字段到服务端

关于分布式事务两阶段提交协议三阶提交协议

关于 服务器提交了协议冲突. Section=ResponseHeader Detail=CR 后面必须是 LF 错误