Robin's Blog

记录 积累 学习 成长

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

本文的客户端应用程序不包括 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 端代码稍微复杂些!

posted on 2009-05-06 12:02  Robin99  阅读(202)  评论(0)    收藏  举报