在测试下面的代码时,线程0下载的文件内容正确,但后面的线程不能下载到正确的数据(我查看了下载文件的二进制数据)(也有可能是拼接的问题)。
注:Logger是一个日志类,Array是一个仿数组类(提供了数组下标访问)
public class ThreadDownload {
private Logger log = new("Download.log");
private string URL; //文件URL
private string Path; //下载后文件保存位置
private long Length; //文件大小(B)
private int Threads; //下载线程数
private long DownloadEnd = 0; //下载完成的字节
private bool Stop = false; //出错停止
private bool NoAccept = false; //目标服务器是否支持多线程下载
private Array<byte[]> DownloadBuffer;
private bool[] Changed;
public ThreadDownload(string dURL,string dPath = null,int dThreads = 64) {
URL = dURL;
Path = dPath;
Threads = dThreads;
Do();
}
private void Do() {
try {
ServicePointManager.DefaultConnectionLimit = Threads + 1;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.SystemDefault | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
Length = WebRequest.Create(URL).GetResponse().ContentLength;
long BlockLong = (Length - Length % Threads) / Threads;
DownloadBuffer = new(Threads+1);
Changed = new bool[Threads+1];
Console.Clear();
Path = Path==null ? new Uri(URL).Segments[new Uri(URL).Segments.Length-1] : Path;
if (File.Exists(Path)) {
File.Delete(Path);
}
{
var readreturntmp1 = ((HttpWebRequest)WebRequest.Create(URL));
readreturntmp1.AddRange(0,1);
var readreturn = (HttpWebResponse)readreturntmp1.GetResponse();
for (int i = 0;i < readreturn.Headers.AllKeys.Length;++i) {
if (readreturn.Headers.AllKeys[i].Equals("Accept-Ranges") && readreturn.Headers[i].Equals("bytes")) {
break;
} else if (readreturn.Headers.AllKeys[i].Equals("Accept-Ranges") && readreturn.Headers[i].Equals("none")) {
NoAccept = true;
break;
}
if (i == readreturn.Headers.AllKeys.Length-1) {
NoAccept = true;
break;
}
}
}
if (!NoAccept) {
Thread[] downThs = new Thread[Threads+1];
Thread pushThs = new Thread(() => {
try {
int Changeds = 0;
var write = new FileStream(Path,FileMode.Append,FileAccess.Write);
for (;Changeds < Threads+1;) {
if (Stop) {
write.Close();
File.Delete(Path);
log = log << "将在单线程重试";
new Download(URL,Path,ref log);
return;
} else if (Changed[Changeds]) {
write.Write(DownloadBuffer[Changeds],0,DownloadBuffer[Changeds].Length);
write.Write(new byte[1]{0},0,1);
write.Flush();
DownloadBuffer[Changeds] = new byte[0];
log = log << "写入" + Changeds + "号线程的数据";
++Changeds;
} else {
Thread.Sleep(200);
}
}
write.Close();
} catch(Exception ex) {
Stop = true;
log = log << new Exception("在合并线程:",ex);
}
});
Thread tjisThs = new Thread(() => {
try {
for (;;) {
long old = DownloadEnd;
Thread.Sleep(200);
Console.Clear();
Console.WriteLine("已完成下载{0}/{2},当前下载速度{1}\\s。",ByteToBytes.ByteToString(DownloadEnd),ByteToBytes.ByteToString(DownloadEnd - old),ByteToBytes.ByteToString(Length));
}
} catch (Exception ex) {
Stop = true;
log = log << new Exception("在统计线程:",ex);
}
});
pushThs.Start();
for (int i = 0;i < downThs.Length;++i) {
downThs[i] = new Thread(info => {
const int ListenBlackLong = 1024*64;
try {
int DownloadBlock = (int)(((DownloadInfo)info).End - ((DownloadInfo)info).Start);
int ID = ((DownloadInfo)info).ID;
DownloadBuffer[ID] = new byte[DownloadBlock];
int key = 0;
Logger log = new("DownloadLog\\" + ID + ".log");
log = log << "下载区间:" + ((DownloadInfo)info).Start + "~" + ((DownloadInfo)info).End + ",共" + DownloadBlock;
for (;key < DownloadBlock;) {
long nowend;
if (DownloadBlock - key < ListenBlackLong) {
nowend = DownloadBlock;
} else {
nowend = ListenBlackLong + key;
}
int nowrange = (int)(nowend - key);
log = log << "读取区间:" + key + "~" + nowend + ",共" + nowrange;
var readstreamt = ((HttpWebRequest)WebRequest.Create(URL));
readstreamt.AddRange(key,nowend);
var readstream = readstreamt.GetResponse().GetResponseStream();
for (int readkey = 0;;) {
if (readkey == nowrange) {
break;
}
lock (DownloadBuffer) {
readkey += readstream.Read(DownloadBuffer[ID],key+readkey,nowrange-readkey);
}
}
readstream.Close();
DownloadEnd += nowrange;
key += nowrange;
}
log.Close();
if (!Stop) {
Changed[ID] = true;
} else {
File.Delete(Path + "-" + ID.ToString());
}
} catch (Exception ex) {
Stop = true;
log = log << new Exception("在" + ((DownloadInfo)info).ID.ToString() + "号线程:",ex);
}
});
var info = new DownloadInfo();
if (i!=downThs.Length-1) {
info.Start = BlockLong * i;
info.End = BlockLong * (i + 1);
} else {
info.Start = Length - Length % Threads;
info.End = Length;
}
info.ID = i;
downThs[i].Start(info);
}
tjisThs.Start();
pushThs.Join();
log.Close();
tjisThs.Abort();
} else {
try {
log = log << "无法多线程下载,开始单线程下载。";
new Download(URL,Path,ref log);
} catch (Exception ex) {
log = log << new Exception("在单线程下载:",ex);
}
}
} catch (Exception ex) {
Stop = true;
log = log << new Exception("在主控制线程:",ex);
log = log << "将在单线程下载中重试";
new Download(URL,Path,ref log);
}
}
private struct DownloadInfo {
public long Start = 0;
public long End = 0;
public int ID = 0;
public DownloadInfo() {
}
}
}