首 页 | 新 闻 | 技术中心 | 第二书店 | 《程序员》 | 《开发高手》 | 社 区 | 黄 页 | 人 才
移 动专 题SUNIBM微 软微 创精 华Donews人 邮
我的技术中心 
我的分类 我的文档
全部文章 发表文章
专栏管理 使用说明



 RSS 订阅 
最新文档列表
Windows/.NET
.NET  (rss)    
Visual C++  (rss)    
Delphi  (rss)    
Visual Basic  (rss)    
ASP  (rss)    
JavaScript  (rss)    
Java/Linux
Java  (rss)    
Perl  (rss)    
综合
其他开发语言  (rss)    
文件格式  (rss)    
企业开发
游戏开发  (rss)    
网站制作技术  (rss)    
数据库
数据库开发  (rss)    
软件工程
其他  (rss)    

积极原创作者 
superyan (27)
iiprogram (83)
nizhigang2000 (3)
hongbo781202 (81)
Kendiv (113)
TechnoFantasy (52)
feifei1018 (19)
coofucoo (108)
qingrun (67)
btbtd (83)
CSDN - 文档中心 - .NET 阅读:9942   评论: 1    参与评论
标题   实现支持文件分块多点异步上传的 Web Services 及其客户端(非Web)应用程序调用相关异步执行的 Web Method     选择自 playyuer 的 Blog
关键字   实现支持文件分块多点异步上传的 Web Services 及其客户端(非Web)应用程序调用相关异步执行的 Web Method
出处  

实现支持文件分块多点异步上传的 Web Services 及其客户端(非Web)应用程序调用相关异步执行的 Web Method

本文的客户端应用程序不包括 ASP.Net Web 应用程序!

本文假设 URL: http://localhost/mywebservices/updownload.asmx

共有 4 个程序文件 (Web.Config 就不赘述了)

Server Side:

标题中所提到的 "异步" 其实在服务器端的程序并没有什么特殊的,而主要是通过客户端应用程序
异步调用相关 Web Method 实现的!

1. updownload.asmx ,位于 IIS 的某个 Web 共享目录,代码如下,只有一句话:

<%@ WebService Language="c#" Codebehind="UpDownLoad.asmx.cs" Class="Service1" %>

2. updownload.asmx.cs ,即: updownload.asmx 的 Codebehind ,位于 IIS 的某个 Web 共享目录的 bin 子目录下,代码如下:

/*

本文件位于 Web 共享目录的 bin 子目录下,通过执行如下命令行编译:
csc /t:library updownload.asmx.cs

*/
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.IO;
using System;

public class Service1 : System.Web.Services.WebService
{
 [WebMethod]
 public string HelloWorld()
 {
  return "Hello World";
 }

 //从 Web Method 本身,其实看不出 "同步" 还是 "异步"
 [WebMethod(Description = "为了支持多点分块异步上传文件,此方法必须由客户端预先调用,以便在服务器端生成指定 FileName 和 Length 大小的空白文件预定空间! 建议客户端同步调用")]
 public string CreateBlankFile(string FileName,int Length) //建议由客户端同步调用
 {
  FileStream fs = new FileStream(Server.MapPath(".") + "\\" + FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
  fs.Write(new byte[Length], 0, Length);
  fs.Close();
  fs = null;
  return FileName + " (" + Length + ") 空白文件已经创建!";
 }

 [WebMethod(Description = "提供一个用于一次完整上传整个文件的方法! 建议客户端同步调用")]
 public string UploadFileBytes(byte[] Bytes,string FileName)
 {
  return UploadFileChunkBytes(Bytes, 0, FileName);
 }

 [WebMethod(Description = "提供一个用于一次只上传由 Position 位置起始的, Bytes 字节的 FileName 文件块存入服务器端相应文件的相应字节位置! 建议客户端异步调用")]
 // 这里只要多提供一个 Position 参数,余下的再由客户端调用异步的该方法,就轻松达到目的了!
 public string UploadFileChunkBytes(byte[] Bytes,int Position,string FileName)
 {
  try
  {
   FileStream fs = new FileStream(Server.MapPath(".") + "\\" + FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
   //该 Bytes 的字节要写到 服务器端 相应文件的从 Position 开始的字节
   fs.Position = Position;
   fs.Write(Bytes, 0, Bytes.Length);
   fs.Close();
   fs = null;
   return FileName + " 文件块: 位置[" + Position + "," + (Position + Bytes.Length) + "] 大小(" + Bytes.Length + ") 上传成功!";
  }
  catch (Exception e)
  {
   return e.Message;
  }
 }

 [WebMethod]
 public byte[] DownloadFileBytes(string FileName)
 {
  if (File.Exists(FileName))
  {
   try
   {
    FileStream fs = File.OpenRead(FileName);
    int i = (int) fs.Length;
    byte[] ba = new byte[i];
    fs.Read(ba,0,i);
    fs.Close();
    return ba;
   }
   catch
   {
    return new byte[0];
   }
  }
  else
  {
   return new byte[0];
  }
 }
}


//=======================================================================

Client Side:
3. UpDownloadProxy.cs :
 本文件由如下命令生成
 % Visual Studio .Net 2003 安装目录下的 %\SDK\v1.1\Bin\wsdl.exe
 具体命令行如下:
 wsdl.exe /l:CS /out:UpDownloadProxy.cs http://localhost/MyWebServices/updownload.asmx?wsdl
 生成的本地的客户端代理类代码里已经为每个 Web Method 生成了可异步和同步执行的方法,例如:
    public string HelloWorld() {}
    public System.IAsyncResult BeginHelloWorld(...) {}
    public string EndHelloWorld(...) {}

 下面是该命令行生成的完整的 UpDownloadProxy.cs 代码,就不修改了:
/*

通过执行如下命令行编译,生成 UpDownloadProxy.dll :
csc /t:library UpDownloadProxy.cs

*/

//------------------------------------------------------------------------------
// <autogenerated>
//     This code was generated by a tool.
//     Runtime Version: 1.1.4322.573
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------------

//
// 此源代码由 wsdl, Version=1.1.4322.573 自动生成。
//
using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Web.Services;


/// <remarks/>
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="Service1Soap", Namespace="http://tempuri.org/")]
public class Service1 : System.Web.Services.Protocols.SoapHttpClientProtocol {
   
    /// <remarks/>
    public Service1() {
        this.Url = "http://localhost/MyWebServices/updownload.asmx";
    }
   
    /// <remarks/>
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/HelloWorld", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    public string HelloWorld() {
        object[] results = this.Invoke("HelloWorld", new object[0]);
        return ((string)(results[0]));
    }
   
    /// <remarks/>
    public System.IAsyncResult BeginHelloWorld(System.AsyncCallback callback, object asyncState) {
        return this.BeginInvoke("HelloWorld", new object[0], callback, asyncState);
    }
   
    /// <remarks/>
    public string EndHelloWorld(System.IAsyncResult asyncResult) {
        object[] results = this.EndInvoke(asyncResult);
        return ((string)(results[0]));
    }
   
    /// <remarks/>
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/CreateBlankFile", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    public string CreateBlankFile(string FileName, int Length) {
        object[] results = this.Invoke("CreateBlankFile", new object[] {
                    FileName,
                    Length});
        return ((string)(results[0]));
    }
   
    /// <remarks/>
    public System.IAsyncResult BeginCreateBlankFile(string FileName, int Length, System.AsyncCallback callback, object asyncState) {
        return this.BeginInvoke("CreateBlankFile", new object[] {
                    FileName,
                    Length}, callback, asyncState);
    }
   
    /// <remarks/>
    public string EndCreateBlankFile(System.IAsyncResult asyncResult) {
        object[] results = this.EndInvoke(asyncResult);
        return ((string)(results[0]));
    }
   
    /// <remarks/>
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/UploadFileBytes", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    public string UploadFileBytes([System.Xml.Serialization.XmlElementAttribute(DataType="base64Binary")] System.Byte[] Bytes, string FileName) {
        object[] results = this.Invoke("UploadFileBytes", new object[] {
                    Bytes,
                    FileName});
        return ((string)(results[0]));
    }
   
    /// <remarks/>
    public System.IAsyncResult BeginUploadFileBytes(System.Byte[] Bytes, string FileName, System.AsyncCallback callback, object asyncState) {
        return this.BeginInvoke("UploadFileBytes", new object[] {
                    Bytes,
                    FileName}, callback, asyncState);
    }
   
    /// <remarks/>
    public string EndUploadFileBytes(System.IAsyncResult asyncResult) {
        object[] results = this.EndInvoke(asyncResult);
        return ((string)(results[0]));
    }
   
    /// <remarks/>
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/UploadFileChunkBytes", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    public string UploadFileChunkBytes([System.Xml.Serialization.XmlElementAttribute(DataType="base64Binary")] System.Byte[] Bytes, int Position, string FileName) {
        object[] results = this.Invoke("UploadFileChunkBytes", new object[] {
                    Bytes,
                    Position,
                    FileName});
        return ((string)(results[0]));
    }
   
    /// <remarks/>
    public System.IAsyncResult BeginUploadFileChunkBytes(System.Byte[] Bytes, int Position, string FileName, System.AsyncCallback callback, object asyncState) {
        return this.BeginInvoke("UploadFileChunkBytes", new object[] {
                    Bytes,
                    Position,
                    FileName}, callback, asyncState);
    }
   
    /// <remarks/>
    public string EndUploadFileChunkBytes(System.IAsyncResult asyncResult) {
        object[] results = this.EndInvoke(asyncResult);
        return ((string)(results[0]));
    }
   
    /// <remarks/>
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/DownloadFileBytes", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    [return: System.Xml.Serialization.XmlElementAttribute(DataType="base64Binary")]
    public System.Byte[] DownloadFileBytes(string FileName) {
        object[] results = this.Invoke("DownloadFileBytes", new object[] {
                    FileName});
        return ((System.Byte[])(results[0]));
    }
   
    /// <remarks/>
    public System.IAsyncResult BeginDownloadFileBytes(string FileName, System.AsyncCallback callback, object asyncState) {
        return this.BeginInvoke("DownloadFileBytes", new object[] {
                    FileName}, callback, asyncState);
    }
   
    /// <remarks/>
    public System.Byte[] EndDownloadFileBytes(System.IAsyncResult asyncResult) {
        object[] results = this.EndInvoke(asyncResult);
        return ((System.Byte[])(results[0]));
    }
}

//=======================================================================
4. UpDownloadClient.cs :
 该程序才是真正实现文件分块多点异步上传的核心代码:

/*

通过执行如下命令行编译:
csc updownloadClient.cs /r:updownloadproxy.dll

*/
using System;
using System.IO;

public class Class1
{
 static void Main(string[] args)
 {
  //Download(ServerSidepath, ClientSidePath)
  Download(@"e:\test.jpg", @"f:\test_local.jpg");
  System.Console.WriteLine("down End");

  System.Console.WriteLine("同步 up file exec ...");
  UploadFile(@"e:\Northwind.mdb");
  System.Console.WriteLine("同步 up file End\n");

  System.Console.WriteLine("异步 up chunks exec ...");
  UploadFileChunks(@"e:\test.rar", 64);
  System.Console.ReadLine();
 }

 public static void UploadFile(string LocalFileName)
 {
  Service1 xx = new Service1();
  FileStream fs = new FileStream(LocalFileName, FileMode.Open); //Client Side Path
  byte[] buffer = new byte[fs.Length];
  fs.Read(buffer, 0, buffer.Length);
  //调用 "同步执行" 的本地 Web Sevices 代理类的 方法,相当于同步调用了 Web Method !
  xx.UploadFileBytes(buffer, System.IO.Path.GetFileName(LocalFileName));
 }

 //指定要上传的本地文件的路径,及每次上传文件块的大小
 public static void UploadFileChunks(string LocalFileName,int ChunkSize)
 {
  Service1 xx = new Service1();
  string filename = System.IO.Path.GetFileName(LocalFileName);

  FileStream fs = new FileStream(LocalFileName, FileMode.Open); //Client Side Path
  //fs = File.OpenRead(LocalFileName);

  int r = (int) fs.Length; //用于记录剩余还未上传的字节数,初值是文件的大小

  //调用 "同步执行" 的本地 Web Sevices 代理类的 方法,相当于同步调用了 Web Method !
  //预定服务器端空间
  xx.CreateBlankFile(filename,r);
  int size = ChunkSize * 1024;
  int k = 0; //用于记录已经上传的字节数
  i++; //用于记录上传的文件块数
  while (r >= size)
  {
   byte[] buffer = new byte[size];
   fs.Read(buffer,0,buffer.Length);
   //调用 "异步执行" 的本地 Web Sevices 代理类的 方法,相当于异步调用了 Web Method !
   //该 buffer 的字节要写到 服务器端 相应文件的从 Position = k 开始的字节
   xx.BeginUploadFileChunkBytes(buffer,k,filename,new AsyncCallback(UploadFileChunkCallback),xx);
   k += size;
   r -= size;
   i++;
  }
  if (r > 0) //剩余的零头
  {
   byte[] buffer = new byte[r];
   fs.Read(buffer,0,buffer.Length);
   //调用 "异步执行" 的本地 Web Sevices 代理类的 方法,相当于异步调用了 Web Method !
   //该 buffer 的字节要写到 服务器端 相应文件的从 Position = k 开始的字节
   xx.BeginUploadFileChunkBytes(buffer,k,filename,new AsyncCallback(UploadFileChunkCallback),xx);
   i++;
  }
  fs.Close();

 }

 private static int i = -1; //用于记录上传的文件块数

 private static void UploadFileChunkCallback(IAsyncResult ar)
 {
  Service1 x = (Service1) ar.AsyncState;
  Console.WriteLine(x.EndUploadFileChunkBytes(ar));
  if ( --i == 0)
  {
   Console.WriteLine("异步 up all chunks end");
  }
 }

 public static void Download(string ServerSideFileName,string LocalFileName)
 {
  Service1 xx = new Service1();
  byte[] ba = xx.DownloadFileBytes(ServerSideFileName); //Server Side Path

  FileStream fs = new FileStream(LocalFileName, FileMode.Create); //Client Side Path
  fs.Write(ba,0,ba.Length);
  fs.Close();
 }
}


//===========================================================================
至此我们通过纯手工的方式完成了任务,之所以不用 VS 就是为了让码子简洁明了!
Microshaoft .Night 就是这么平易近人! (PMPMP to MS)
通过 Web Sevices 上传文件非常简单,甚至比传统的 http Web 上床还简单!
同时较轻松地就实现了文件分块多点异步上传:
Server 端代码没啥特殊的!
Client 端代码稍微复杂些!


相关文章
对该文的评论
CSDN 网友 ( 2004-12-17)
有什么P意义??
操JB蛋