用WebClient.UploadData方法上载文件数据的方法

  假如某网站有个表单,例如(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 + "--rn"; //结束边界

  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>

  /// -----------------------------7d52ee27210a3crnContent-Disposition: form-data; name="表单名"rnrn表单值rn

  /// </remarks>

  public byte[] CreateFieldData(string fieldName, string fieldValue)

  ...{

  string textTemplate = Boundary + "rnContent-Disposition: form-data; name="{0}"rnrn{1}rn";

  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 = "rn";

  string textTemplate = Boundary + "rnContent-Disposition: form-data; name="{0}"; filename="{1}"rnContent-Type: {2}rnrn";

  // 头数据

  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); // rn

  return fieldData;

  }

  属性#region 属性

  public string Boundary

  ...{

  get

  ...{

  string[] bArray, ctArray;

  string contentType = ContentType;

  ctArray = contentType.Split(';');

  if (ctArray[0].Trim().ToLower() == "multipart/form-data")

  ...{

  bArray = ctArray[1].Split('=');

  return "--" + bArray[1];

  }

  return null;

  }

  }

  public string ContentType

  ...{

  get ...{

  if (HttpContext.Current == null)

  ...{

  return "multipart/form-data; boundary=---------------------------7d5b915500cee";

  }

  return HttpContext.Current.Request.ContentType;

  }

  }

  #endregion

  }

  }

  在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:response.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;

  }

  }

  }

  }

  完整的代码见附件: UploadData.rar(38K)(http://bbs.openlab.net.cn/PostAttachment.aspx?PostID=400927),解压后给web目录建个虚拟目录"UploadData",其中UploadAvatar.aspx是实际的上传处理页,如果上传成功,则返回文件名和文件类型等信息。default.aspx是asp.net页面来调用 WebClient.UploadData方法提交数据,UploadDataWin项目则是winform程序调用。