在WebService的帮助下,进行多线程上传文件是非常简单。因此我只做个简单的例子,那么如果想要实现此功能的朋友,可以在我的基础上进行扩展。
首先说说服务器端,只需要提供一个能允许多线程写文件的函数即可,具体代码如下。
[WebMethod]
public bool UploadFileData( string FileName, int StartPosition, byte[] bData )
{
string strFullName = Server.MapPath( "Uploads" ) + @"\" + FileName;
FileStream fs = null;
try
{
fs = new FileStream( strFullName, FileMode.OpenOrCreate,
FileAccess.Write, FileShare.Write );
}
catch( IOException err )
{
Session["ErrorMessage"] = err.Message;
return false;
}
using( fs )
{
fs.Position = StartPosition;
fs.Write( bData, 0, bData.Length );
}
return true;
}
其中“Uploads”是在服务程序所在目录下的一个子目录,需要设置ASPNET用户对此目录具有可写权限。
相对于服务器端来说,客户端要稍微复杂一些,因为要牵扯到多线程的问题。为了更好的传递参数,我用一个线程类来完成。具体如下。
public delegate void UploadFileData( string FileName, int StartPos, byte[] bData );
/// <summary>
/// FileThread: a class for sub-thread
/// </summary>
sealed class FileThread
{
private int nStartPos;
private int nTotalBytes;
private string strFileName;
public static UploadFileData UploadHandle;
/// <summary>
/// Constructor
/// </summary>
/// <param name="StartPos"></param>
/// <param name="TotalBytes"></param>
/// <param name="FileName"></param>
public FileThread( int StartPos, int TotalBytes, string FileName )
{
//Init thread variant
nStartPos = StartPos;
nTotalBytes = TotalBytes;
strFileName = FileName;
//Only for debug
Debug.WriteLine( string.Format( "File name:{0} position: {1} total byte:{2}",
strFileName, nStartPos, nTotalBytes ) );
}
/// <summary>
/// Sub-thread entry function
/// </summary>
/// <param name="stateinfo"></param>
public void UploadFile( object stateinfo )
{
int nRealRead, nBufferSize;
const int BUFFER_SIZE = 10240;
using( FileStream fs = new FileStream( strFileName,
FileMode.Open, FileAccess.Read,
FileShare.Read ) )
{
string sName = strFileName.Substring( strFileName.LastIndexOf( "\\" ) + 1 );
byte[] bBuffer = new byte[BUFFER_SIZE];//Init 10k buffer
fs.Position = nStartPos;
nRealRead = 0;
do
{
nBufferSize = BUFFER_SIZE;
if( nRealRead + BUFFER_SIZE > nTotalBytes )
nBufferSize = nTotalBytes - nRealRead;
nBufferSize = fs.Read( bBuffer, 0, nBufferSize );
if( nBufferSize == BUFFER_SIZE )
UploadHandle( sName,
nRealRead + nStartPos,
bBuffer );
else if( nBufferSize > 0 )
{
//Copy data
byte[] bytData = new byte[nBufferSize];
Array.Copy( bBuffer,0, bytData, 0, nBufferSize );
UploadHandle( sName,
nRealRead + nStartPos,
bytData );
}
nRealRead += nBufferSize;
}
while( nRealRead < nTotalBytes );
}
//Release signal
ManualResetEvent mr = stateinfo as ManualResetEvent;
if( mr != null )
mr.Set();
}
}
那么在执行的时候,要创建线程类对象,并为每一个每个线程设置一个信号量,从而能在所有线程都结束的时候得到通知,大致的代码如下。
FileInfo fi = new FileInfo( txtFileName.Text );
if( fi.Exists )
{
btnUpload.Enabled = false;//Avoid upload twice
//Init signals
ManualResetEvent[] events = new ManualResetEvent[5];
//Devide blocks
int nTotalBytes = (int)( fi.Length / 5 );
for( int i = 0; i < 5; i++ )
{
events[i] = new ManualResetEvent( false );
FileThread thdSub = new FileThread(
i * nTotalBytes,
( fi.Length - i * nTotalBytes ) > nTotalBytes ? nTotalBytes:(int)( fi.Length - i * nTotalBytes ),
fi.FullName );
ThreadPool.QueueUserWorkItem( new WaitCallback( thdSub.UploadFile ), events[i] );
}
//Wait for threads finished
WaitHandle.WaitAll( events );
//Reset button status
btnUpload.Enabled = true;
}
总体来说,程序还是相对比较简单,而我也只是做了个简单例子而已,一些细节都没有进行处理。
本来想打包提供给大家下载,没想到CSDN的Blog对于这点做的太差,老是异常。
如下是客户端的完整代码。
//--------------------------- Multi-thread Upload Demo ---------------------------------------
//--------------------------------------------------------------------------------------------
//---File: frmUpload
//---Description: The multi-thread upload form file to demenstrate howto use multi-thread to
// upload files
//---Author: Knight
//---Date: Oct.12, 2006
//--------------------------------------------------------------------------------------------
//---------------------------{Multi-thread Upload Demo}---------------------------------------
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace CSUpload
{
using System.IO;
using System.Diagnostics;
using System.Threading;
using WSUploadFile;//Web-service reference namespace
/// <summary>
/// Summary description for Form1.
/// </summary>
public class frmUpload : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox txtFileName;
private System.Windows.Forms.Button btnBrowse;
private System.Windows.Forms.Button btnUpload;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public frmUpload()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.txtFileName = new System.Windows.Forms.TextBox();
this.btnBrowse = new System.Windows.Forms.Button();
this.btnUpload = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// txtFileName
//
this.txtFileName.Location = new System.Drawing.Point(16, 24);
this.txtFileName.Name = "txtFileName";
this.txtFileName.Size = new System.Drawing.Size(248, 20);
this.txtFileName.TabIndex = 0;
this.txtFileName.Text = "";
//
// btnBrowse
//
this.btnBrowse.Location = new System.Drawing.Point(272, 24);
this.btnBrowse.Name = "btnBrowse";
this.btnBrowse.TabIndex = 1;
this.btnBrowse.Text = "&Browse...";
this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
//
// btnUpload
//
this.btnUpload.Location = new System.Drawing.Point(272, 56);
this.btnUpload.Name = "btnUpload";
this.btnUpload.TabIndex = 2;
this.btnUpload.Text = "&Upload";
this.btnUpload.Click += new System.EventHandler(this.btnUpload_Click);
//
// frmUpload
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(370, 111);
this.Controls.Add(this.btnUpload);
this.Controls.Add(this.btnBrowse);
this.Controls.Add(this.txtFileName);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.Name = "frmUpload";
this.Text = "Upload";
this.Load += new System.EventHandler(this.frmUpload_Load);
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
Application.Run(new frmUpload());
}
private FileUpload myUpload = new FileUpload();
private void UploadData( string FileName, int StartPos, byte[] bData )
{
//Call web service upload
myUpload.UploadFileData( FileName, StartPos, bData );
}
private void btnUpload_Click(object sender, System.EventArgs e)
{
FileInfo fi = new FileInfo( txtFileName.Text );
if( fi.Exists )
{
btnUpload.Enabled = false;//Avoid upload twice
//Init signals
ManualResetEvent[] events = new ManualResetEvent[5];
//Devide blocks
int nTotalBytes = (int)( fi.Length / 5 );
for( int i = 0; i < 5; i++ )
{
events[i] = new ManualResetEvent( false );
FileThread thdSub = new FileThread(
i * nTotalBytes,
( fi.Length - i * nTotalBytes ) > nTotalBytes ? nTotalBytes:(int)( fi.Length - i * nTotalBytes ),
fi.FullName );
ThreadPool.QueueUserWorkItem( new WaitCallback( thdSub.UploadFile ), events[i] );
}
//Wait for threads finished
WaitHandle.WaitAll( events );
//Reset button status
btnUpload.Enabled = true;
}
}
private void frmUpload_Load(object sender, System.EventArgs e)
{
FileThread.UploadHandle = new UploadFileData( this.UploadData );
}
private void btnBrowse_Click(object sender, System.EventArgs e)
{
if( fileOpen.ShowDialog() == DialogResult.OK )
txtFileName.Text = fileOpen.FileName;
}
private OpenFileDialog fileOpen = new OpenFileDialog();
}
public delegate void UploadFileData( string FileName, int StartPos, byte[] bData );
/// <summary>
/// FileThread: a class for sub-thread
/// </summary>
sealed class FileThread
{
private int nStartPos;
private int nTotalBytes;
private string strFileName;
public static UploadFileData UploadHandle;
/// <summary>
/// Constructor
/// </summary>
/// <param name="StartPos"></param>
/// <param name="TotalBytes"></param>
/// <param name="FileName"></param>
public FileThread( int StartPos, int TotalBytes, string FileName )
{
//Init thread variant
nStartPos = StartPos;
nTotalBytes = TotalBytes;
strFileName = FileName;
//Only for debug
Debug.WriteLine( string.Format( "File name:{0} position: {1} total byte:{2}",
strFileName, nStartPos, nTotalBytes ) );
}
/// <summary>
/// Sub-thread entry function
/// </summary>
/// <param name="stateinfo"></param>
public void UploadFile( object stateinfo )
{
int nRealRead, nBufferSize;
const int BUFFER_SIZE = 10240;
using( FileStream fs = new FileStream( strFileName,
FileMode.Open, FileAccess.Read,
FileShare.Read ) )
{
string sName = strFileName.Substring( strFileName.LastIndexOf( "\\" ) + 1 );
byte[] bBuffer = new byte[BUFFER_SIZE];//Init 10k buffer
fs.Position = nStartPos;
nRealRead = 0;
do
{
nBufferSize = BUFFER_SIZE;
if( nRealRead + BUFFER_SIZE > nTotalBytes )
nBufferSize = nTotalBytes - nRealRead;
nBufferSize = fs.Read( bBuffer, 0, nBufferSize );
if( nBufferSize == BUFFER_SIZE )
UploadHandle( sName,
nRealRead + nStartPos,
bBuffer );
else if( nBufferSize > 0 )
{
//Copy data
byte[] bytData = new byte[nBufferSize];
Array.Copy( bBuffer,0, bytData, 0, nBufferSize );
UploadHandle( sName,
nRealRead + nStartPos,
bytData );
}
nRealRead += nBufferSize;
}
while( nRealRead < nTotalBytes );
}
//Release signal
ManualResetEvent mr = stateinfo as ManualResetEvent;
if( mr != null )
mr.Set();
}
}
}