测试代码:
using UnityEngine;
using System;
public class NewBehaviourScript1 : MonoBehaviour {
void Start () {
try
{
WebClientEx webClient = new WebClientEx(5);
webClient.DownloadString("https://www.baidu.com/");
}
catch (Exception e)
{
Debug.Log(e);
}
}
}
扩展函数:
using System.Collections.Specialized;
using System.ComponentModel;
using System.Net;
using System;
using System.IO;
public class DownloadProgressChangedEventArgsEx
{
public long BytesReceived { get; private set; }
public long TotalBytesToReceive { get; private set; }
public float ProgressPercentage { get; private set; }
public object UserState;
public DownloadProgressChangedEventArgsEx(long received, long toReceive, float progressPercentage, object userState)
{
BytesReceived = received;
TotalBytesToReceive = toReceive;
ProgressPercentage = progressPercentage;
UserState = userState;
}
}
public class WebClientEx : WebClient
{
public string m_Url;
public int m_TimeOut;
private DownloadFileRangeState state;
public delegate void DownloadProgressChangedEventHandlerEx(object sender,DownloadProgressChangedEventArgsEx e);
public event DownloadProgressChangedEventHandlerEx DownloadProgressChangedEx;
public delegate void AsyncCompletedEventHandlerEx(object sender, AsyncCompletedEventArgs e);
public event AsyncCompletedEventHandlerEx DownloadFileCompletedEx;
public WebClientEx(int timeout)
{
m_TimeOut = 1000 * timeout;
}
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request;
if (address.Scheme == "https")
{
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => { return true; };
request = (HttpWebRequest)base.GetWebRequest(address);
request.ProtocolVersion = HttpVersion.Version10;
}
else
{
request = (HttpWebRequest)base.GetWebRequest(address);
}
request.Timeout = m_TimeOut;
request.ReadWriteTimeout = m_TimeOut;
m_Url = address.OriginalString;
request.AllowAutoRedirect = false;
request.AllowWriteStreamBuffering = true;
CookieContainer cookieContainer = new CookieContainer();
NameValueCollection collection = new NameValueCollection();
collection.Add("Accept-Language", "zh-cn,zh;q=0.5");
collection.Add("Accept-Encoding", "gzip,deflate");
collection.Add("Accept-Charset", "GB2312,utf-8;q=0.7,*;q=0.7");
collection.Add("Keep-Alive", "115");
request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13";
request.Headers.Add(collection);
request.CookieContainer = cookieContainer;
request.ServicePoint.BindIPEndPointDelegate = (servicePoint, remoteEndPoint, retryCount) =>
{
if (remoteEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
return new IPEndPoint(IPAddress.IPv6Any, 0);
else
return new IPEndPoint(IPAddress.Any, 0);
};
return request;
}
public new void CancelAsync()
{
base.CancelAsync();
if (state!=null && state.m_isRangeDownload) state.m_isRangeDownload = false;
}
public void DownloadFileRangeAsync(string url, string path)
{
DownloadFileRangeAsync(url, path, null);
}
/// <summary>
/// 异步断点续传
/// </summary>
/// <param name="url"></param>
/// <param name="path"></param>
/// <param name="userState"></param>
public void DownloadFileRangeAsync(string url, string path, object userState)
{
if (state != null && state.m_isRangeDownload)
return;
Loom.RunAsync(()=> {
state = new DownloadFileRangeState(path,userState);
state.onCompleted =()=> DownloadFileCompletedEx;
state.m_isRangeDownload = true;
long startPos = CheckFile(state);
if (startPos == -1) return;
try
{
state.m_Request = (HttpWebRequest)GetWebRequest(new Uri(url));
state.m_Request.ReadWriteTimeout = m_TimeOut;
state.m_Request.Timeout = m_TimeOut;
if (startPos > 0) state.m_Request.AddRange((int)startPos);
state.m_Respone = state.m_Request.GetResponse();
state.m_Stream = state.m_Respone.GetResponseStream();
var totalBytesReceived = state.m_Respone.ContentLength + startPos;
var bytesReceived = startPos;
//如果下载完成
if (totalBytesReceived != 0 && bytesReceived >= totalBytesReceived)
{
state.Close();
try
{
if (File.Exists(state.m_Path)) File.Delete(state.m_Path);
File.Move(state.m_TempPath, state.m_Path);
}
catch (Exception e)
{
state.m_Exception = e;
state.Close();
}
}
else
{
WriteFile(state, startPos);
}
}
catch (Exception e)
{
state.m_Exception = e;
}
finally
{
state.Close();
}
});
}
/// <summary>
/// 检查文件
/// </summary>
/// <param name="state"></param>
/// <returns></returns>
private long CheckFile(DownloadFileRangeState state)
{
long startPos = 0;
if (File.Exists(state.m_TempPath))
{
state.m_FileStream = File.OpenWrite(state.m_TempPath);
startPos = state.m_FileStream.Length;
state.m_FileStream.Seek(startPos, SeekOrigin.Current);
}
else
{
try
{
string direName = Path.GetDirectoryName(state.m_TempPath);
if (!Directory.Exists(direName)) Directory.CreateDirectory(direName);
state.m_FileStream = new FileStream(state.m_TempPath, FileMode.Create);
}
catch (Exception e)
{
state.m_Exception = e;
startPos = -1;
state.Close();
}
}
return startPos;
}
/// <summary>
/// 写入文件
/// </summary>
/// <param name="state"></param>
/// <param name="startPos"></param>
private void WriteFile(DownloadFileRangeState state, long startPos)
{
var bytesReceived = startPos;
byte[] bytes = new byte[1024];
bool isDownloadCompleted = false;
var totalBytesReceived = state.m_Respone.ContentLength + startPos;
int readSize = state.m_Stream.Read(bytes, 0, 1024);
while (readSize > 0 && state.m_isRangeDownload)
{
lock (state.m_FileStream)
{
Loom.QueueOnMainThread(() =>
{
if (DownloadProgressChangedEx != null)
DownloadProgressChangedEx(this, new DownloadProgressChangedEventArgsEx(bytesReceived, totalBytesReceived, ((float)bytesReceived / totalBytesReceived), state.m_UserState));
});
state.m_FileStream.Write(bytes, 0, readSize);// 将下载到的数据写入临时文件
bytesReceived += readSize;
// 判断是否下载完成
// 下载完成将temp文件,改成正式文件
if (totalBytesReceived != 0 && bytesReceived >= totalBytesReceived)
{
state.Close();
try
{
if (File.Exists(state.m_Path)) File.Delete(state.m_Path);
File.Move(state.m_TempPath, state.m_Path);
isDownloadCompleted = true;
}
catch (Exception e)
{
state.m_Exception = e;
state.Close();
}
}
readSize = state.m_Stream.Read(bytes, 0, 1024);
}
}
if (!isDownloadCompleted)
{
state.m_Exception = new Exception("Request for early closure");
}
}
private class DownloadFileRangeState
{
private readonly string tmpSuffix = ".temp";
public Func<AsyncCompletedEventHandlerEx> onCompleted;
public HttpWebRequest m_Request = null;
public WebResponse m_Respone = null;
public Stream m_Stream = null;
public FileStream m_FileStream = null;
public Exception m_Exception = null;
public bool m_isRangeDownload;
public string m_TempPath;
public string m_Path;
public object m_UserState;
public DownloadFileRangeState(string path, object userState)
{
this.m_Path = path;
this.m_UserState = userState;
m_TempPath = path + tmpSuffix;
}
public void Close()
{
if (m_FileStream != null)
{
m_FileStream.Flush();
m_FileStream.Close();
m_FileStream = null;
}
if (m_Stream != null) m_Stream.Close();
if (m_Respone != null) m_Respone.Close();
if (m_Request != null) m_Request.Abort();
Loom.QueueOnMainThread(() => {
if (m_Exception != null) GameError.NetworkError(m_Exception.Message);
if (onCompleted() != null)
onCompleted()(this, new AsyncCompletedEventArgs(m_Exception, true, m_UserState));
});
}
}
}