欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

asp.net下实现支持文件分块多点异步上传的 Web Services

程序员文章站 2022-06-14 11:40:03
本文的客户端应用程序不包括 asp.net web 应用程序! 本文假设 url: http://localhost/my...
本文的客户端应用程序不包括 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 端代码稍微复杂些!