当前位置: 移动技术网 > IT编程>开发语言>.net > C#实现支持断点续传多线程下载客户端工具类

C#实现支持断点续传多线程下载客户端工具类

2017年12月12日  | 移动技术网IT编程  | 我要评论
复制代码 代码如下: /* .net/c#: 实现支持断点续传多线程下载的 http web 客户端工具类 (c# diy httpwebclient) * reflect
复制代码 代码如下:

/* .net/c#: 实现支持断点续传多线程下载的 http web 客户端工具类 (c# diy httpwebclient)
* reflector 了一下 system.net.webclient ,改写或增加了若干:
* download、upload 相关方法!
* download 相关改动较大!
* 增加了 datareceive、exceptionoccurrs 事件!
* 了解服务器端与客户端交互的 http 协议参阅:
* 使文件下载的自定义连接支持 flashget 的断点续传多线程链接下载! jsp/servlet 实现!
* http://blog.csdn.net/playyuer/archive/2004/08/02/58430.aspx
* 使文件下载的自定义连接支持 flashget 的断点续传多线程链接下载! c#/asp.net 实现!
* http://blog.csdn.net/playyuer/archive/2004/08/02/58281.aspx
*/
//2005-03-14 修订:
/* .net/c#: 实现支持断点续传多线程下载的工具类
* reflector 了一下 system.net.webclient ,改写或增加了若干:
* download、upload 相关方法!
* 增加了 datareceive、exceptionoccurrs 事件
*/
namespace microshaoft.utils
{
using system;
using system.io;
using system.net;
using system.text;
using system.security;
using system.threading;
using system.collections.specialized;
/// <summary>
/// 记录下载的字节位置
/// </summary>
public class downloadstate
{
private string _filename;
private string _attachmentname;
private int _position;
private string _requesturl;
private string _responseurl;
private int _length;
private byte[] _data;
public string filename
{
get
{
return _filename;
}
}
public int position
{
get
{
return _position;
}
}
public int length
{
get
{
return _length;
}
}
public string attachmentname
{
get
{
return _attachmentname;
}
}
public string requesturl
{
get
{
return _requesturl;
}
}
public string responseurl
{
get
{
return _responseurl;
}
}
public byte[] data
{
get
{
return _data;
}
}
internal downloadstate(string requesturl, string responseurl, string filename, string attachmentname, int position, int length, byte[] data)
{
this._filename = filename;
this._requesturl = requesturl;
this._responseurl = responseurl;
this._attachmentname = attachmentname;
this._position = position;
this._data = data;
this._length = length;
}
internal downloadstate(string requesturl, string responseurl, string filename, string attachmentname, int position, int length, threadcallbackhandler tch)
{
this._requesturl = requesturl;
this._responseurl = responseurl;
this._filename = filename;
this._attachmentname = attachmentname;
this._position = position;
this._length = length;
this._threadcallback = tch;
}
internal downloadstate(string requesturl, string responseurl, string filename, string attachmentname, int position, int length)
{
this._requesturl = requesturl;
this._responseurl = responseurl;
this._filename = filename;
this._attachmentname = attachmentname;
this._position = position;
this._length = length;
}
private threadcallbackhandler _threadcallback;
public httpwebclient httpwebclient
{
get
{
return this._hwc;
}
set
{
this._hwc = value;
}
}
internal thread thread
{
get
{
return _thread;
}
set
{
_thread = value;
}
}
private httpwebclient _hwc;
private thread _thread;
//
internal void startdownloadfilechunk()
{
if (this._threadcallback != null)
{
this._threadcallback(this._requesturl, this._filename, this._position, this._length);
this._hwc.onthreadprocess(this._thread);
}
}
}
//委托代理线程的所执行的方法签名一致
public delegate void threadcallbackhandler(string s, string s, int i, int i);
//异常处理动作
public enum exceptionactions
{
throw,
cancelall,
ignore,
retry
}
/// <summary>
/// 包含 exception 事件数据的类
/// </summary>
public class exceptioneventargs : system.eventargs
{
private system.exception _exception;
private exceptionactions _exceptionaction;
private downloadstate _downloadstate;
public downloadstate downloadstate
{
get
{
return _downloadstate;
}
}
public exception exception
{
get
{
return _exception;
}
}
public exceptionactions exceptionaction
{
get
{
return _exceptionaction;
}
set
{
_exceptionaction = value;
}
}
internal exceptioneventargs(system.exception e, downloadstate downloadstate)
{
this._exception = e;
this._downloadstate = downloadstate;
}
}
/// <summary>
/// 包含 download 事件数据的类
/// </summary>
public class downloadeventargs : system.eventargs
{
private downloadstate _downloadstate;
public downloadstate downloadstate
{
get
{
return _downloadstate;
}
}
public downloadeventargs(downloadstate downloadstate)
{
this._downloadstate = downloadstate;
}
}
public class threadprocesseventargs : system.eventargs
{
private thread _thread;
public thread thread
{
get
{
return this._thread;
}
}
public threadprocesseventargs(thread thread)
{
this._thread = thread;
}
}
/// <summary>
/// 支持断点续传多线程下载的类
/// </summary>
public class httpwebclient
{
private static object _synclockobject = new object();
public delegate void datareceiveeventhandler(httpwebclient sender, downloadeventargs e);
public event datareceiveeventhandler datareceive; //接收字节数据事件
public delegate void exceptioneventhandler(httpwebclient sender, exceptioneventargs e);
public event exceptioneventhandler exceptionoccurrs; //发生异常事件
public delegate void threadprocesseventhandler(httpwebclient sender, threadprocesseventargs e);
public event threadprocesseventhandler threadprocessend; //发生多线程处理完毕事件
private int _filelength; //下载文件的总大小
public int filelength
{
get
{
return _filelength;
}
}
/// <summary>
/// 分块下载文件
/// </summary>
/// <param name="address">url 地址</param>
/// <param name="filename">保存到本地的路径文件名</param>
/// <param name="chunkscount">块数,线程数</param>
public void downloadfile(string address, string filename, int chunkscount)
{
int p = 0; // position
int s = 0; // chunk size
string a = null;
httpwebrequest hwrq;
httpwebresponse hwrp = null;
try
{
hwrq = (httpwebrequest) webrequest.create(this.geturi(address));
hwrp = (httpwebresponse) hwrq.getresponse();
long l = hwrp.contentlength;
hwrq.credentials = this.m_credentials;
l = ((l == -1) || (l > 0x7fffffff)) ? ((long) 0x7fffffff) : l; //int32.maxvalue 该常数的值为 2,147,483,647; 即十六进制的 0x7fffffff
int l = (int) l;
this._filelength = l;
// 在本地预定空间(竟然在多线程下不用先预定空间)
// filestream sw = new filestream(filename, filemode.openorcreate, fileaccess.readwrite, fileshare.readwrite);
// sw.write(new byte[l], 0, l);
// sw.close();
// sw = null;
bool b = (hwrp.headers["accept-ranges"] != null & hwrp.headers["accept-ranges"] == "bytes");
a = hwrp.headers["content-disposition"]; //attachment
if (a != null)
{
a = a.substring(a.lastindexof("filename=") + 9);
}
else
{
a = filename;
}
int ss = s;
if (b)
{
s = l / chunkscount;
if (s < 2 * 64 * 1024) //块大小至少为 128 k 字节
{
s = 2 * 64 * 1024;
}
ss = s;
int i = 0;
while (l > s)
{
l -= s;
if (l < s)
{
s += l;
}
if (i++ > 0)
{
downloadstate x = new downloadstate(address, hwrp.responseuri.absolutepath, filename, a, p, s, new threadcallbackhandler(this.downloadfilechunk));
// 单线程下载
// x.startdownloadfilechunk();
x.httpwebclient = this;
//多线程下载
thread t = new thread(new threadstart(x.startdownloadfilechunk));
//this.onthreadprocess(t);
t.start();
}
p += s;
}
s = ss;
byte[] buffer = this.responseasbytes(address, hwrp, s, filename);
this.onthreadprocess(thread.currentthread);
// lock (_synclockobject)
// {
// this._bytes += buffer.length;
// }
}
}
catch (exception e)
{
exceptionactions ea = exceptionactions.throw;
if (this.exceptionoccurrs != null)
{
downloadstate x = new downloadstate(address, hwrp.responseuri.absolutepath, filename, a, p, s);
exceptioneventargs eea = new exceptioneventargs(e, x);
exceptionoccurrs(this, eea);
ea = eea.exceptionaction;
}
if (ea == exceptionactions.throw)
{
if (!(e is webexception) && !(e is securityexception))
{
throw new webexception("net_webclient", e);
}
throw;
}
}
}
internal void onthreadprocess(thread t)
{
if (threadprocessend != null)
{
threadprocesseventargs tpea = new threadprocesseventargs(t);
threadprocessend(this, tpea);
}
}
/// <summary>
/// 下载一个文件块,利用该方法可自行实现多线程断点续传
/// </summary>
/// <param name="address">url 地址</param>
/// <param name="filename">保存到本地的路径文件名</param>
/// <param name="length">块大小</param>
public void downloadfilechunk(string address, string filename, int fromposition, int length)
{
httpwebresponse hwrp = null;
string a = null;
try
{
//this._filename = filename;
httpwebrequest hwrq = (httpwebrequest) webrequest.create(this.geturi(address));
//hwrq.credentials = this.m_credentials;
hwrq.addrange(fromposition);
hwrp = (httpwebresponse) hwrq.getresponse();
a = hwrp.headers["content-disposition"]; //attachment
if (a != null)
{
a = a.substring(a.lastindexof("filename=") + 9);
}
else
{
a = filename;
}
byte[] buffer = this.responseasbytes(address, hwrp, length, filename);
// lock (_synclockobject)
// {
// this._bytes += buffer.length;
// }
}
catch (exception e)
{
exceptionactions ea = exceptionactions.throw;
if (this.exceptionoccurrs != null)
{
downloadstate x = new downloadstate(address, hwrp.responseuri.absolutepath, filename, a, fromposition, length);
exceptioneventargs eea = new exceptioneventargs(e, x);
exceptionoccurrs(this, eea);
ea = eea.exceptionaction;
}
if (ea == exceptionactions.throw)
{
if (!(e is webexception) && !(e is securityexception))
{
throw new webexception("net_webclient", e);
}
throw;
}
}
}
internal byte[] responseasbytes(string requesturl, webresponse response, long length, string filename)
{
string a = null; //attachmentname
int p = 0; //整个文件的位置指针
int num2 = 0;
try
{
a = response.headers["content-disposition"]; //attachment
if (a != null)
{
a = a.substring(a.lastindexof("filename=") + 9);
}
long num1 = length; //response.contentlength;
bool flag1 = false;
if (num1 == -1)
{
flag1 = true;
num1 = 0x10000; //64k
}
byte[] buffer1 = new byte[(int) num1];
int p = 0; //本块的位置指针
string s = response.headers["content-range"];
if (s != null)
{
s = s.replace("bytes ", "");
s = s.substring(0, s.indexof("-"));
p = convert.toint32(s);
}
int num3 = 0;
stream s = response.getresponsestream();
do
{
num2 = s.read(buffer1, num3, ((int) num1) - num3);
num3 += num2;
if (flag1 && (num3 == num1))
{
num1 += 0x10000;
byte[] buffer2 = new byte[(int) num1];
buffer.blockcopy(buffer1, 0, buffer2, 0, num3);
buffer1 = buffer2;
}
// lock (_synclockobject)
// {
// this._bytes += num2;
// }
if (num2 > 0)
{
if (this.datareceive != null)
{
byte[] buffer = new byte[num2];
buffer.blockcopy(buffer1, p, buffer, 0, buffer.length);
downloadstate dls = new downloadstate(requesturl, response.responseuri.absolutepath, filename, a, p, num2, buffer);
downloadeventargs dlea = new downloadeventargs(dls);
//触发事件
this.ondatareceive(dlea);
//system.threading.thread.sleep(100);
}
p += num2; //本块的位置指针
p += num2; //整个文件的位置指针
}
else
{
break;
}
}
while (num2 != 0);
s.close();
s = null;
if (flag1)
{
byte[] buffer3 = new byte[num3];
buffer.blockcopy(buffer1, 0, buffer3, 0, num3);
buffer1 = buffer3;
}
return buffer1;
}
catch (exception e)
{
exceptionactions ea = exceptionactions.throw;
if (this.exceptionoccurrs != null)
{
downloadstate x = new downloadstate(requesturl, response.responseuri.absolutepath, filename, a, p, num2);
exceptioneventargs eea = new exceptioneventargs(e, x);
exceptionoccurrs(this, eea);
ea = eea.exceptionaction;
}
if (ea == exceptionactions.throw)
{
if (!(e is webexception) && !(e is securityexception))
{
throw new webexception("net_webclient", e);
}
throw;
}
return null;
}
}
private void ondatareceive(downloadeventargs e)
{
//触发数据到达事件
datareceive(this, e);
}
public byte[] uploadfile(string address, string filename)
{
return this.uploadfile(address, "post", filename, "file");
}
public string uploadfileex(string address, string method, string filename, string fieldname)
{
return encoding.ascii.getstring(uploadfile(address, method, filename, fieldname));
}
public byte[] uploadfile(string address, string method, string filename, string fieldname)
{
byte[] buffer4;
filestream stream1 = null;
try
{
filename = path.getfullpath(filename);
string text1 = "---------------------" + datetime.now.ticks.tostring("x");
string text2 = "application/octet-stream";
stream1 = new filestream(filename, filemode.open, fileaccess.read);
webrequest request1 = webrequest.create(this.geturi(address));
request1.credentials = this.m_credentials;
request1.contenttype = "multipart/form-data; boundary=" + text1;
request1.method = method;
string[] textarray1 = new string[7] {"--", text1, "\r\ncontent-disposition: form-data; name=\"" + fieldname + "\"; filename=\"", path.getfilename(filename), "\"\r\ncontent-type: ", text2, "\r\n\r\n"};
string text3 = string.concat(textarray1);
byte[] buffer1 = encoding.utf8.getbytes(text3);
byte[] buffer2 = encoding.ascii.getbytes("\r\n--" + text1 + "\r\n");
long num1 = 0x7fffffffffffffff;
try
{
num1 = stream1.length;
request1.contentlength = (num1 + buffer1.length) + buffer2.length;
}
catch
{
}
byte[] buffer3 = new byte[math.min(0x2000, (int) num1)];
using (stream stream2 = request1.getrequeststream())
{
int num2;
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;
webresponse response1 = request1.getresponse();
buffer4 = this.responseasbytes(response1);
}
catch (exception exception1)
{
if (stream1 != null)
{
stream1.close();
stream1 = null;
}
if (!(exception1 is webexception) && !(exception1 is securityexception))
{
//throw new webexception(sr.getstring("net_webclient"), exception1);
throw new webexception("net_webclient", exception1);
}
throw;
}
return buffer4;
}
private byte[] responseasbytes(webresponse response)
{
int num2;
long num1 = response.contentlength;
bool flag1 = false;
if (num1 == -1)
{
flag1 = true;
num1 = 0x10000;
}
byte[] buffer1 = new byte[(int) num1];
stream stream1 = response.getresponsestream();
int num3 = 0;
do
{
num2 = stream1.read(buffer1, num3, ((int) num1) - num3);
num3 += num2;
if (flag1 && (num3 == num1))
{
num1 += 0x10000;
byte[] buffer2 = new byte[(int) num1];
buffer.blockcopy(buffer1, 0, buffer2, 0, num3);
buffer1 = buffer2;
}
}
while (num2 != 0);
stream1.close();
if (flag1)
{
byte[] buffer3 = new byte[num3];
buffer.blockcopy(buffer1, 0, buffer3, 0, num3);
buffer1 = buffer3;
}
return buffer1;
}
private namevaluecollection m_requestparameters;
private uri m_baseaddress;
private icredentials m_credentials = credentialcache.defaultcredentials;
public icredentials credentials
{
get
{
return this.m_credentials;
}
set
{
this.m_credentials = value;
}
}
public namevaluecollection querystring
{
get
{
if (this.m_requestparameters == null)
{
this.m_requestparameters = new namevaluecollection();
}
return this.m_requestparameters;
}
set
{
this.m_requestparameters = value;
}
}
public string baseaddress
{
get
{
if (this.m_baseaddress != null)
{
return this.m_baseaddress.tostring();
}
return string.empty;
}
set
{
if ((value == null) || (value.length == 0))
{
this.m_baseaddress = null;
}
else
{
try
{
this.m_baseaddress = new uri(value);
}
catch (exception exception1)
{
throw new argumentexception("value", exception1);
}
}
}
}
private uri geturi(string path)
{
uri uri1;
try
{
if (this.m_baseaddress != null)
{
uri1 = new uri(this.m_baseaddress, path);
}
else
{
uri1 = new uri(path);
}
if (this.m_requestparameters == null)
{
return uri1;
}
stringbuilder builder1 = new stringbuilder();
string text1 = string.empty;
for (int num1 = 0; num1 < this.m_requestparameters.count; num1++)
{
builder1.append(text1 + this.m_requestparameters.allkeys[num1] + "=" + this.m_requestparameters[num1]);
text1 = "&";
}
uribuilder builder2 = new uribuilder(uri1);
builder2.query = builder1.tostring();
uri1 = builder2.uri;
}
catch (uriformatexception)
{
uri1 = new uri(path.getfullpath(path));
}
return uri1;
}
}
}
/// <summary>
/// 测试类
/// </summary>
class apptest
{
int _k = 0;
int _k = 0;
static void main()
{
apptest a = new apptest();
microshaoft.utils.httpwebclient x = new microshaoft.utils.httpwebclient();
a._k = 10;
//订阅 datareceive 事件
x.datareceive += new microshaoft.utils.httpwebclient.datareceiveeventhandler(a.x_datareceive);
//订阅 exceptionoccurrs 事件
x.exceptionoccurrs += new microshaoft.utils.httpwebclient.exceptioneventhandler(a.x_exceptionoccurrs);
x.threadprocessend += new microshaoft.utils.httpwebclient.threadprocesseventhandler(a.x_threadprocessend);
string f = "http://localhost/download/phpmyadmin-2.6.1-pl2.zip";
f = "http://down6.flashget.com/flashget182cn.exe";
a._f = f;
string f = f.substring(f.lastindexof("/") + 1);
//(new system.threading.thread(new system.threading.threadstart(new threadprocessstate(f, @"e:\temp\" + f, 10, x).startthreadprocess))).start();
x.downloadfile(f, @"d:\temp\" + f, a._k);
// x.downloadfilechunk(f, @"e:\temp\" + f,15,34556);
system.console.readline();
// string uploadfile = "e:\\test_local.rar";
// string str = x.uploadfileex("http://localhost/phpmyadmin/uploadaction.php", "post", uploadfile, "file1");
// system.console.writeline(str);
// system.console.readline();
}
string bs = ""; //用于记录上次的位数
bool b = false;
private int i = 0;
private static object _synclockobject = new object();
string _f;
string _f;
private void x_datareceive(microshaoft.utils.httpwebclient sender, microshaoft.utils.downloadeventargs e)
{
if (!this.b)
{
lock (_synclockobject)
{
if (!this.b)
{
system.console.write(system.datetime.now.tostring() + " 已接收数据: ");
//system.console.write( system.datetime.now.tostring() + " 已接收数据: ");
this.b = true;
}
}
}
string f = e.downloadstate.filename;
if (e.downloadstate.attachmentname != null)
f = system.io.path.getdirectoryname(f) + @"\" + e.downloadstate.attachmentname;
this._f = f;
using (system.io.filestream sw = new system.io.filestream(f, system.io.filemode.openorcreate, system.io.fileaccess.readwrite, system.io.fileshare.readwrite))
{
sw.position = e.downloadstate.position;
sw.write(e.downloadstate.data, 0, e.downloadstate.data.length);
sw.close();
}
string s = system.datetime.now.tostring();
lock (_synclockobject)
{
this.i += e.downloadstate.data.length;
system.console.write(bs + "\b\b\b\b\b\b\b\b\b\b"+ i + " / " + sender.filelength + " 字节数据 " + s);
//system.console.write(bs + i + " 字节数据 " + s);
this.bs = new string('\b', digits(i) + 3 + digits(sender.filelength) + s.length);
}
}
int digits(int n) //数字所占位数
{
n = system.math.abs(n);
n = n / 10;
int i = 1;
while (n > 0)
{
n = n / 10;
i++;
}
return i;
}
private void x_exceptionoccurrs(microshaoft.utils.httpwebclient sender, microshaoft.utils.exceptioneventargs e)
{
system.console.writeline(e.exception.message);
//发生异常重新下载相当于断点续传,你可以自己自行选择处理方式
microshaoft.utils.httpwebclient x = new microshaoft.utils.httpwebclient();
x.downloadfilechunk(this._f, this._f, e.downloadstate.position, e.downloadstate.length);
e.exceptionaction = microshaoft.utils.exceptionactions.ignore;
}
private void x_threadprocessend(microshaoft.utils.httpwebclient sender, microshaoft.utils.threadprocesseventargs e)
{
//if (e.thread.threadstate == system.threading.threadstate.stopped)
if (this._k ++ == this._k - 1)
system.console.writeline("\nend");
}
}

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网