zoukankan      html  css  js  c++  java
  • [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 + "-- ";
          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 + " Content-Disposition: form-data; name="{0}" {1} ";
          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 = " ";
          string httpRow = "--" + boundary + " Content-Disposition: form-data; name="{0}"; filename="{1}" Content-Type: {2} ";
          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: est.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 + "-- "; //结束边界
                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 Content-Disposition: form-data; name="表单名" 表单值
            /// </remarks>
            public byte[] CreateFieldData(string fieldName, string fieldValue)
            {
                string textTemplate = Boundary + " Content-Disposition: form-data; name="{0}" {1} ";
                string text = String.Format(textTemplate, fieldName, fieldValue);
                byte[] bytes = encoding.GetBytes(text);
                return bytes;
            }

            
            /**//// <summary>
            /// 获取文件上传表单区域二进制数组
            /// </summary>
            /// <param name="fieldName">表单名</param>
            /// <param name="filename">文件名</param>
            /// <param name="contentType">文件类型</param>
            /// <param name="contentLength">文件长度</param>
            /// <param name="stream">文件流</param>
            /// <returns>二进制数组</returns>
            public byte[] CreateFieldData(string fieldName, string filename,string contentType, byte[] fileBytes)
            {
                string end = " ";
                string textTemplate = Boundary + " Content-Disposition: form-data; name="{0}"; filename="{1}" Content-Type: {2} ";
                
                // 头数据
                string data = String.Format(textTemplate, fieldName, filename, contentType);
                byte[] bytes = encoding.GetBytes(data);

                

                // 尾数据
                byte[] endBytes = encoding.GetBytes(end);

                // 合成后的数组
                byte[] fieldData = new byte[bytes.Length + fileBytes.Length + endBytes.Length];

                bytes.CopyTo(fieldData, 0); // 头数据
                fileBytes.CopyTo(fieldData, bytes.Length); // 文件的二进制数据
                endBytes.CopyTo(fieldData, bytes.Length + fileBytes.Length); // 

                return fieldData;
            }


            属性属性
        }
    }


    在Winform中调用


    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;

    using UploadData.Common;
    using System.IO;

    namespace UploadDataWin
    {
        /**//// <summary>
        /// frmUpload 的摘要说明。
        /// </summary>
        public class frmUpload : System.Windows.Forms.Form
        {
            private System.Windows.Forms.Label lblAmigoToken;
            private System.Windows.Forms.TextBox txtAmigoToken;
            private System.Windows.Forms.Label lblFilename;
            private System.Windows.Forms.TextBox txtFilename;
            private System.Windows.Forms.Button btnBrowse;
            private System.Windows.Forms.TextBox txtFileData;
            private System.Windows.Forms.Label lblFileData;
            private System.Windows.Forms.Button btnUpload;
            private System.Windows.Forms.OpenFileDialog openFileDialog1;
            private System.Windows.Forms.TextBox txtResponse;
            /**//// <summary>
            /// 必需的设计器变量。
            /// </summary>
            private System.ComponentModel.Container components = null;

            public frmUpload()
            {
                //
                // Windows 窗体设计器支持所必需的
                //
                InitializeComponent();

                //
                // TODO: 在 InitializeComponent 调用后添加任何构造函数代码
                //
            }

            /**//// <summary>
            /// 清理所有正在使用的资源。
            /// </summary>
            protected override void Dispose( bool disposing )
            {
                if( disposing )
                {
                    if (components != null) 
                    {
                        components.Dispose();
                    }
                }
                base.Dispose( disposing );
            }

            Windows 窗体设计器生成的代码#region Windows 窗体设计器生成的代码
            /**//// <summary>
            /// 设计器支持所需的方法 - 不要使用代码编辑器修改
            /// 此方法的内容。
            /// </summary>
            private void InitializeComponent()
            {
                this.lblAmigoToken = new System.Windows.Forms.Label();
                this.txtAmigoToken = new System.Windows.Forms.TextBox();
                this.lblFilename = new System.Windows.Forms.Label();
                this.txtFilename = new System.Windows.Forms.TextBox();
                this.btnBrowse = new System.Windows.Forms.Button();
                this.txtFileData = new System.Windows.Forms.TextBox();
                this.lblFileData = new System.Windows.Forms.Label();
                this.btnUpload = new System.Windows.Forms.Button();
                this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
                this.txtResponse = new System.Windows.Forms.TextBox();
                this.SuspendLayout();
                // 
                // lblAmigoToken
                // 
                this.lblAmigoToken.Location = new System.Drawing.Point(40, 48);
                this.lblAmigoToken.Name = "lblAmigoToken";
                this.lblAmigoToken.Size = new System.Drawing.Size(72, 23);
                this.lblAmigoToken.TabIndex = 0;
                this.lblAmigoToken.Text = "AmigoToken";
                // 
                // txtAmigoToken
                // 
                this.txtAmigoToken.Location = new System.Drawing.Point(120, 48);
                this.txtAmigoToken.Name = "txtAmigoToken";
                this.txtAmigoToken.Size = new System.Drawing.Size(248, 21);
                this.txtAmigoToken.TabIndex = 1;
                this.txtAmigoToken.Text = "";
                // 
                // lblFilename
                // 
                this.lblFilename.Location = new System.Drawing.Point(40, 96);
                this.lblFilename.Name = "lblFilename";
                this.lblFilename.Size = new System.Drawing.Size(80, 23);
                this.lblFilename.TabIndex = 2;
                this.lblFilename.Text = "Filename";
                // 
                // txtFilename
                // 
                this.txtFilename.Location = new System.Drawing.Point(120, 96);
                this.txtFilename.Name = "txtFilename";
                this.txtFilename.Size = new System.Drawing.Size(248, 21);
                this.txtFilename.TabIndex = 3;
                this.txtFilename.Text = "";
                // 
                // btnBrowse
                // 
                this.btnBrowse.Location = new System.Drawing.Point(296, 144);
                this.btnBrowse.Name = "btnBrowse";
                this.btnBrowse.TabIndex = 4;
                this.btnBrowse.Text = "浏览";
                this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
                // 
                // txtFileData
                // 
                this.txtFileData.Location = new System.Drawing.Point(120, 144);
                this.txtFileData.Name = "txtFileData";
                this.txtFileData.Size = new System.Drawing.Size(168, 21);
                this.txtFileData.TabIndex = 5;
                this.txtFileData.Text = "";
                // 
                // lblFileData
                // 
                this.lblFileData.Location = new System.Drawing.Point(40, 144);
                this.lblFileData.Name = "lblFileData";
                this.lblFileData.Size = new System.Drawing.Size(72, 23);
                this.lblFileData.TabIndex = 6;
                this.lblFileData.Text = "FileData";
                // 
                // btnUpload
                // 
                this.btnUpload.Location = new System.Drawing.Point(48, 184);
                this.btnUpload.Name = "btnUpload";
                this.btnUpload.TabIndex = 7;
                this.btnUpload.Text = "Upload";
                this.btnUpload.Click += new System.EventHandler(this.btnUpload_Click);
                // 
                // txtResponse
                // 
                this.txtResponse.Location = new System.Drawing.Point(136, 184);
                this.txtResponse.Multiline = true;
                this.txtResponse.Name = "txtResponse";
                this.txtResponse.Size = new System.Drawing.Size(248, 72);
                this.txtResponse.TabIndex = 8;
                this.txtResponse.Text = "";
                // 
                // frmUpload
                // 
                this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
                this.ClientSize = new System.Drawing.Size(400, 269);
                this.Controls.Add(this.txtResponse);
                this.Controls.Add(this.btnUpload);
                this.Controls.Add(this.lblFileData);
                this.Controls.Add(this.txtFileData);
                this.Controls.Add(this.btnBrowse);
                this.Controls.Add(this.txtFilename);
                this.Controls.Add(this.lblFilename);
                this.Controls.Add(this.txtAmigoToken);
                this.Controls.Add(this.lblAmigoToken);
                this.Name = "frmUpload";
                this.Text = "frmUpload";
                this.ResumeLayout(false);

            }
            #endregion

            /**//// <summary>
            /// 应用程序的主入口点。
            /// </summary>
            [STAThread]
            static void Main() 
            {
                Application.Run(new frmUpload());
            }

            private void btnUpload_Click(object sender, System.EventArgs e)
            {
                // 非空检验
                if (txtAmigoToken.Text.Trim() == "" || txtFilename.Text == "" || txtFileData.Text.Trim() == "")
                {
                    MessageBox.Show("Please fill data");
                    return;
                }

                // 所要上传的文件路径
                string path = txtFileData.Text.Trim();

                // 检查文件是否存在
                if (!File.Exists(path)) 
                {
                    MessageBox.Show("{0} does not exist!", path);
                    return;
                }

                // 读文件流
                FileStream fs = new FileStream(path, FileMode.Open,
                    FileAccess.Read, FileShare.Read);
                
                // 这部分需要完善
                string ContentType = "application/octet-stream";
                byte[] fileBytes = new byte[fs.Length];
                fs.Read(fileBytes, 0, Convert.ToInt32(fs.Length));


                // 生成需要上传的二进制数组
                CreateBytes cb = new CreateBytes();
                // 所有表单数据
                ArrayList bytesArray = new ArrayList();
                // 普通表单
                bytesArray.Add(cb.CreateFieldData("FileName", txtFilename.Text));
                bytesArray.Add(cb.CreateFieldData("AmigoToken", txtAmigoToken.Text));
                // 文件表单
                bytesArray.Add(cb.CreateFieldData("FileData", path
                                                    , ContentType, fileBytes));

                // 合成所有表单并生成二进制数组
                byte[] bytes = cb.JoinBytes(bytesArray);
                
                // 返回的内容
                byte[] responseBytes;
                
                // 上传到指定Url
                bool uploaded = cb.UploadData("http://localhost/UploadData/UploadAvatar.aspx", bytes, out responseBytes);

                // 将返回的内容输出到文件
                using (FileStream file = new FileStream(@"c: esponse.text", FileMode.Create, FileAccess.Write, FileShare.Read))
                {
                    file.Write(responseBytes, 0, responseBytes.Length);
                }

                txtResponse.Text = System.Text.Encoding.UTF8.GetString(responseBytes);

            }

            private void btnBrowse_Click(object sender, System.EventArgs e)
            {
                if(openFileDialog1.ShowDialog() == DialogResult.OK)
                {
                    txtFileData.Text = openFileDialog1.FileName;
                }

            }
        }
    }

  • 相关阅读:
    Eclipse+Java+OpenCV246人脸识别
    Eclipse+Java+OpenCV246环境搭建和代码测试
    Request对象和Response对象详解
    request与response对象详述
    request对象和response对象
    减少HTTP请求之将图片转成二进制并生成Base64编码,可以在网页中通过url查看图片(大型网站优化技术)
    base64码通过http传输 +号变 空格 问题解决
    RSA Android加密的数据服务器上无法解密?
    RSA解密时javax.crypto.BadPaddingException: Data must start with zero
    SRA解密报错:Data must start with zero
  • 原文地址:https://www.cnblogs.com/hf-0712/p/5918455.html
Copyright © 2011-2022 走看看