(总结2)WinForm中3种方式文件上传服务器:WebClient

时间:2021-09-04 14:28:40

转:http://www.cnblogs.com/lmjob/archive/2008/10/14/1310617.html

相信用ASP.NET写一个上传文件的网页,大家都会写,但是有没有人想过通过在WinForm中通过HTTP协议上传文件呢?
有些人说要向服务器端上传文件,用FTP协议不是很简单吗?效率又高,为什么还要使用HTTP协议那么麻烦呢?这里面有几个原因:
1FTP服务器的部署相对麻烦,还要设置权限,权限设置不对,还会惹来一系列的安全问题。
2)如果双方都还有防火墙,又不想开发FTP相关的一些端口时,HTTP就会大派用场,就像WEB Services能穿透防火墙一样。

但是使用HTTP也有他的一些问题,例如不能断点续传,大文件上传很难,速度很慢,所以HTTP协议上传的文件大小不应该太大。
说了这么多,原归正传,一般来说,在Winform里通过HTTP上传文件有几种可选的方法:
1)前面提到的Web Services,就是一种很好的方法,通过编写一个WebMethod,包含有 byte[] 类型的参数,然后调用Web Services的方法,文件内容就会以Base64编码传到服务器上,然后重新保存即可。
[WebMethod]
public void UploadFile(byte[] content,string filename){
           Stream sw = new StreamWriter(...);
           sw.Close();
}
当然,这种通过Base64编码的方法效率比较低,那么可以采用WSE,支持附件,并以2进制形式传送,效率会更高。

(2)除了通过WebService,另外一种更简单的方法就是通过WebClient或者HttpWebRequest来模拟HTTP的POST动作来实现。

这时候首先需要编写一个asp.net web form来响应上传,代码如下:
<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="UploadFileWeb.WebForm1" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html>
 <head>
  <title>WebForm1</title>
  <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
  <meta name="CODE_LANGUAGE" Content="C#">
  <meta name="vs_defaultClientScript" content="JavaScript">
  <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
 </head>
 <body>
  <form id="Form1" method="post" runat="server">
  </form>
 </body>
</html>

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace UploadFileWeb
{
 /// <summary>
 /// WebForm1 的摘要说明。
 /// </summary>
 public class WebForm1 : System.Web.UI.Page
 {
  private void Page_Load(object sender, System.EventArgs e)
  {
   // 在此处放置用户代码以初始化页面
   foreach( string f in Request.Files.AllKeys)
   {
    HttpPostedFile file = Request.Files[f];
    file.SaveAs(@"D:\Temp\" + file.FileName);
   }
   if( Request.Params["testKey"] != null )
   {
    Response.Write(Request.Params["testKey"]);
   }
  }

  #region Web 窗体设计器生成的代码
  override protected void OnInit(EventArgs e)
  {
   //
   // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
   //
   InitializeComponent();
   base.OnInit(e);
  }
  
  /// <summary>
  /// 设计器支持所需的方法 - 不要使用代码编辑器修改
  /// 此方法的内容。
  /// </summary>
  private void InitializeComponent()
  {   
   this.Load += new System.EventHandler(this.Page_Load);
  }
  #endregion
 }
}

其实这个页面跟我们平常写的asp.net上传文件代码是一样的,在Web 页的Request对象中包含有Files这个对象,里面就包含了通过POST方式上传的所有文件的信息,这时所需要做的就是调用 Request.Files[i].SaveAs方法。

但是怎么让才能在WinForm里面模拟想Web Form POST 数据呢?System.Net命名空间里面提供了两个非常有用的类,一个是WebClient,另外一个是HttpWebRequest类。如果我们不需要通过代理服务器来上传文件,那么非常简单,只需要简单的调用WebClient.UploadFile方法就能实现上传文件(Winform界面后台代码):
 private void button1_Click(object sender, System.EventArgs e)
  {
         WebClient myWebClient = new WebClient();
   
         myWebClient.UploadFile("
http://localhost/UploadFileWeb/WebForm1.aspx","POST",@"D:\Temp\Java\JavaStart\JavaStart2.exe");
       }

是不是觉得很简单呢?确实就这么简单。

但是如果要通过代理服务器上传又怎么办呢?那就需要使用到HttpWebRequest,但是该类没有Upload方法,但是幸运的是我们通过Reflector反编译了WebClient.UploadFile方法后,我们发现其内部也是通过WebRequest来实现的,代码如下:
public byte[] UploadFile(string address, string method, string fileName)
{
      string text1;
      string text2;
      WebRequest request1;
      string text3;
      byte[] buffer1;
      byte[] buffer2;
      long num1;
      byte[] buffer3;
      int num2;
      WebResponse response1;
      byte[] buffer4;
      DateTime time1;
      long num3;
      string[] textArray1;
      FileStream stream1 = null;
      try
      {
            fileName = Path.GetFullPath(fileName);
            time1 = DateTime.Now;
            num3 = time1.Ticks;
            text1 = "---------------------" + num3.ToString("x");
            if (this.m_headers == null)
            {
                  this.m_headers = new WebHeaderCollection();
            }
            text2 = this.m_headers["Content-Type"];
            if (text2 != null)
            {
                  if (text2.ToLower(CultureInfo.InvariantCulture).StartsWith("multipart/"))
                  {
                        throw new WebException(SR.GetString("net_webclient_Multipart"));
                  }
            }
            else
            {
                  text2 = "application/octet-stream";
            }
            this.m_headers["Content-Type"] = "multipart/form-data; boundary=" + text1;
            this.m_responseHeaders = null;
            stream1 = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            request1 = WebRequest.Create(this.GetUri(address));
            request1.Credentials = this.Credentials;
            this.CopyHeadersTo(request1);
            request1.Method = method;
            textArray1 = new string[7];
            textArray1[0] = "--";
            textArray1[1] = text1;
            textArray1[2] = "\r\nContent-Disposition: form-data; name=\"file\"; filename=\"";
            textArray1[3] = Path.GetFileName(fileName);
            textArray1[4] = "\"\r\nContent-Type: ";
            textArray1[5] = text2;
            textArray1[6] = "\r\n\r\n";
            text3 = string.Concat(textArray1);
            buffer1 = Encoding.UTF8.GetBytes(text3);
            buffer2 = Encoding.ASCII.GetBytes("\r\n--" + text1 + "\r\n");
            num1 = 9223372036854775807;
            try
            {
                  num1 = stream1.Length;
                  request1.ContentLength = ((num1 + ((long) buffer1.Length)) + ((long) buffer2.Length));
            }
            catch
            {
            }
            buffer3 = new byte[Math.Min(((int) 8192), ((int) num1))];
            using (Stream stream2 = request1.GetRequestStream())
            {
                  stream2.Write(buffer1, 0, buffer1.Length);
                  do
                  {
                        num2 = stream1.Read(buffer3, 0, buffer3.Length);
                        if (num2 != 0)
                        {
                              stream2.Write(buffer3, 0, num2);
                        }
                  }
                  while ((num2 != 0));
                  stream2.Write(buffer2, 0, buffer2.Length);
            }
            stream1.Close();
            stream1 = null;
            response1 = request1.GetResponse();
            this.m_responseHeaders = response1.Headers;
            return this.ResponseAsBytes(response1);
      }
      catch (Exception exception1)
      {
            if (stream1 != null)
            {
                  stream1.Close();
                  stream1 = null;
            }
            if ((exception1 is WebException) || (exception1 is SecurityException))
            {
                  throw;
            }
            throw new WebException(SR.GetString("net_webclient"), exception1);
      }
      return buffer4;
}

 
在这段代码里面其实最关键的就是如何模拟POST请求,通过分析代码和监视HTTP,我们可以发现模拟的POST格式如下:

-----------------------8c64f47716481f0  //时间戳

Content-Disposition: form-data; name="file"; filename="a.txt"  //文件名

Content-Type: application/octet-stream

 

//文件的内容

-----------------------8c64f47716481f0

 

这时候,我们只需自己编码来模拟这么一组数据就行(我们还可以好好借鉴MS的代码呢),以下就是代码(声明一下,我是借用了别人的代码)