using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
namespace HttpDownload
{
class HttpDownloader
{
const int bytebuff = 4096;
const int ReadWriteTimeOut = 10 * 1000;// 在写入超时或读取超时之前的毫秒数
const int TimeOutWait = 10 * 1000;// 请求超时前等待的毫秒数
const int MaxTryTime = 10; // 最大重试次数
int currentRetryTimes = 0;
private bool finished = false;
public bool Finished { get { return finished; } }
private long totalRemoteFileSize = 0L; // 要下载的文件的总大小
public long TotalRemoteFileSize { get { return totalRemoteFileSize; } }
public long BytesWritten { get { return offset; } }
string url, destPath;
private long offset, timeStamp, bytesAtTimeStamp;
volatile private int speed;
FileStream fs;
internal volatile bool running = false;
public HttpDownloader(string url, string destPath, long offset = 0)
{
this.destPath = destPath;
this.url = url;
this.offset = offset;
}
public void DownloadFile()
{
if (isDownloadFinished(offset))
{
return;
}
try
{
for (; !finished && running;)
HttpDownloadFile();
}
catch (Exception ex)
{
throw ex;
}
}
const string errorInfo = "下载结束, 但是文件长度与下载字节数不一致!";
///
/// 下载文件(同步) 支持断点续传
///
private bool HttpDownloadFile()
{
//打开上次下载的文件
//long lStartPos = 0;
fs = getFileStream();
HttpWebRequest request = null;
WebResponse respone = null;
Stream ns = null;
try
{
request = (HttpWebRequest)WebRequest.Create(url);
request.ReadWriteTimeout = ReadWriteTimeOut;
request.Timeout = TimeOutWait;
if (offset > 0)
request.AddRange(offset);//设置Range值,断点续传
//向服务器请求,获得服务器回应数据流
respone = request.GetResponse();
ns = respone.GetResponseStream();
byte[] nbytes = new byte[bytebuff];
int nReadSize = ns.Read(nbytes, 0, bytebuff);
if (timeStamp == 0)
{
timeStamp = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds);
bytesAtTimeStamp = offset;
}
while (nReadSize > 0 && running)
{
long now = Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds);
if (now - timeStamp >= 1000)
{
long dataDiff = offset - bytesAtTimeStamp, timeDiff = now - timeStamp;
speed = (int)(dataDiff * 1000 / timeDiff); // 1000是由ms转为s
timeStamp = now;
bytesAtTimeStamp = offset;
}
fs.Write(nbytes, 0, nReadSize);
fs.Flush();
offset += nReadSize;
//Thread.Sleep(20);
nReadSize = ns.Read(nbytes, 0, bytebuff);
}
if (!running)
return true;
if (offset != totalRemoteFileSize && running)//文件长度不等于下载长度,下载出错
{
throw new Exception(errorInfo);
}
if (running)
finished = true;
}
catch (Exception ex)
{
if (ex.Message.Equals(errorInfo))
throw ex;
++currentRetryTimes;
if (currentRetryTimes > MaxTryTime)
{
throw ex;
}
}
finally
{
if (ns != null)
ns.Close();
if (fs != null)
fs.Close();
if (request != null)
request.Abort();
}
return false;
}
private FileStream getFileStream()
{
if (fs != null)
{
fs.Close();
}
if (File.Exists(destPath))
{
fs = File.OpenWrite(destPath);
// offset < 0 则按照文件的大小定为offset
if (offset < 0)
{
offset = fs.Length;
}
fs.Seek(offset, SeekOrigin.Current);//移动文件流中的当前指针
}
else
{
string dirName = Path.GetDirectoryName(destPath);
if (!Directory.Exists(dirName))//如果不存在保存文件夹路径,新建文件夹
{
Directory.CreateDirectory(dirName);
}
fs = new FileStream(destPath, FileMode.Create);
offset = 0;
}
return fs;
}
///
/// 获取下载文件长度
///
///
///
public static long GetFileContentLength(string url)
{
HttpWebRequest request = null;
try
{
request = (HttpWebRequest)HttpWebRequest.Create(url);
request.Timeout = TimeOutWait;
request.ReadWriteTimeout = ReadWriteTimeOut;
//向服务器请求,获得服务器回应数据流
WebResponse respone = request.GetResponse();
request.Abort();
return respone.ContentLength;
}
catch (Exception e)
{
if (request != null)
request.Abort();
return 0;
}
}
private bool isDownloadFinished(long offset)
{
if (finished)
return true;
if (totalRemoteFileSize == 0)
totalRemoteFileSize = GetFileContentLength(url);
if (totalRemoteFileSize != 0 && totalRemoteFileSize == offset)
{
//下载完成
finished = true;
return true;
}
return false;
}
///
/// 获取当前速度
///
///
///
public string getSpeed(bool formatted)
{
if (formatted)
{
if (finished)
return "0 kB/s";
else
{
return formatSpeed(speed, 0L, 0);
}
}
else
{
if (finished)
return "0";
else
return speed + "";
}
}
private string formatSpeed(long speed, long decimalDigit, int depth)
{
if (speed < 1024)
{
return handleDigit(speed, decimalDigit) + getUnitByDepth(depth);
}
else
{
return formatSpeed(speed / 1024, speed % 1024, depth + 1);
}
}
private string handleDigit(long integer, long decimalDigit)
{
if (decimalDigit == 0)
{
return integer.ToString();
}
else
{
return integer.ToString() + Math.Round((double)decimalDigit / 1024, 1).ToString().Substring(1);
}
}
private string getUnitByDepth(int depth)
{
string val = string.Empty;
switch (depth)
{
case 0:
val = " B/s";
break;
case 1:
val = " kB/s";
break;
case 2:
val = " MB/s";
break;
case 3:
val = " GB/s";
break;
default:
val = " unknown/s";
break;
}
return val;
}
public double getPercentage(int decimals)
{
if (totalRemoteFileSize == 0L)
{
return Math.Round(0d, decimals);
}
if (finished)
return Math.Round(100d, decimals);
double percentage = (double)offset * 100d / totalRemoteFileSize;
return Math.Round(percentage, decimals);
}
}
}