unity http multithread download

base http protocal  & Loom 

 
  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using UnityEngine;
using System.IO;
using System.Threading;

 
  

public class HttpDownloadItem
{
const int MBYTES = 1024 * 1024;

 
  

public enum State
{
INITIAL,
DOWNLOADING,
ERROR,
COMPLETE
}

 
  

public string m_URL;
public string m_StoragePath;
public State m_State;
public long m_FileLength;

 
  

int m_DownloadedBytesCount;
long m_ThreadsPreDownloadBytes;
FileStream m_FileStream;

 
  

object m_WriteLock;
bool m_Disposed;
long m_StartIndex;
int m_ThreadsNum;
AutoResetEvent m_AutoReset = new AutoResetEvent(false);

 
  

public HttpDownloadItem(string url, string storagePath)
{
m_URL = url;
m_StoragePath = storagePath;
m_State = State.INITIAL;
m_WriteLock = new object();
}

 
  

public void Reset()
{
m_State = State.INITIAL;
}

 
  

public void Start()
{
m_State = State.DOWNLOADING;
Loom.RunAsync(() => {
InitDownloadLength();
StartDownload();
});
}

 
  

public void Dispose()
{
m_Disposed = true;
if (m_FileStream != null)
m_FileStream.Close();
}

 
  

void InitDownloadLength()
{
try
{
var request = WebRequest.GetHttpWebRequest(m_URL);
request.Timeout = 10000;
request.SendChunked = false;
request.Method = WebRequestMethods.Http.Head;
var response = request.GetResponse();
m_FileLength = response.ContentLength;
m_FileStream = File.Open(m_StoragePath, FileMode.OpenOrCreate, FileAccess.Write);
m_FileStream.SetLength(0);
response.Close();
request.Abort();
}
catch (Exception e)
{
m_State = State.ERROR;
MainThreadLog(e.ToString());
m_FileStream.Close();
throw e;
}
}

 
  

void StartDownload()
{
while (m_FileLength - m_ThreadsPreDownloadBytes > 0)
{
if (m_Disposed)
break;

 
  

if (Loom.Current.IsThreadsMax())
{
if (m_ThreadsNum > 0)
m_AutoReset.WaitOne();

 
  

continue;
}

 
  

long length = 0;
var remain = m_FileLength - m_ThreadsPreDownloadBytes;
if (remain > MBYTES)
{
m_ThreadsPreDownloadBytes += MBYTES;
length = MBYTES;
}
else
{
m_ThreadsPreDownloadBytes += remain;
length = remain;
}

 
  

var start = m_StartIndex;
Interlocked.Increment(ref m_ThreadsNum);
Loom.RunAsync(() => Down((int)start, (int)length));
m_StartIndex += length;
}

 
  

while (m_ThreadsNum > 0)
continue;

 
  

CompleteDownload();
}

 
  

void Down(int startIndex, int length, int retryTime = 0)
{
if (retryTime > 0)
MainThreadLog("下载线程重试 : " + retryTime.ToString());

 
  

if (retryTime == 3)
{
m_State = State.ERROR;
throw new Exception("one thread download three times fail.");
}

 
  

try
{
if (m_Disposed)
return;

 
  

var request = WebRequest.GetHttpWebRequest(m_URL);
request.Timeout = 10000;
request.AddRange(startIndex, startIndex + length - 1);
request.ServicePoint.ConnectionLimit = int.MaxValue;
request.SendChunked = false;
request.Method = WebRequestMethods.Http.Get;
var response = request.GetResponse();
var stream = response.GetResponseStream();
stream.ReadTimeout = 10000;
var buffer = new byte[MBYTES];
var count = 0;
var totalCount = 0;
while ((count = stream.Read(buffer, 0, buffer.Length)) > 0)
{
lock (m_WriteLock)
{
m_FileStream.Seek(startIndex + totalCount, SeekOrigin.Begin);
m_FileStream.Write(buffer, 0, count);
m_DownloadedBytesCount += count;
}

 
  

UnityEngine.Debug.LogError((float)m_DownloadedBytesCount / (MBYTES));
totalCount += count;
if (totalCount >= length)
break;
}

 
  

stream.Close();
response.Close();
request.Abort();
}
catch (Exception e)
{
MainThreadLog(e.ToString());
Down(startIndex, length, retryTime + 1);
}
finally
{
m_AutoReset.Set();
Interlocked.Decrement(ref m_ThreadsNum);
}
}

 
  

void CompleteDownload()
{
try
{
m_FileStream.Flush();

 
  

if (m_DownloadedBytesCount == m_FileLength)
{
m_State = State.COMPLETE;
UnityEngine.Debug.Log("Download OK : " + m_URL);
}
else
{
UnityEngine.Debug.Log("Download ERROR : " + m_URL);
m_State = State.ERROR;
}
}
finally
{
Dispose();
}
}

 
  

void MainThreadLog(string s)
{
Loom.QueueOnMainThread(() => {
UnityEngine.Debug.LogError(string.Format("download exception : {0} \n URL : {1}", s, m_URL));
});
}
}

 
  

public class HttpDownload
{
Dictionary m_URLAndPaths;
Queue m_DownloadQueue;
Action m_OnComplete;
Action m_OnError;
bool m_Disposed;

 
  

public HttpDownload(Dictionary urlAndPaths, Action onComplete, Action onError)
{
m_URLAndPaths = urlAndPaths;
m_DownloadQueue = new Queue();
foreach (var pair in urlAndPaths)
{
var item = new HttpDownloadItem(pair.Key, pair.Value);
m_DownloadQueue.Enqueue(item);
}

 
  

m_OnComplete = onComplete;
m_OnError = onError;
}

 
  

~HttpDownload()
{
Dispose();
}

 
  

public void Dispose()
{
m_Disposed = true;
m_URLAndPaths = null;
}

 
  

public void Retry()
{
if (m_DownloadQueue.Count <= 0)
return;

 
  

var item = m_DownloadQueue.Peek();
item.Reset();
Run();
}

 
  

public void Run()
{
Loom.RunAsync(() => {
while (true)
{
// 队列为空,结束下载
if (m_DownloadQueue.Count == 0)
{
Loom.QueueOnMainThread(() => {
if (m_OnComplete != null)
m_OnComplete();
});
break;
}

 
  

if (m_Disposed)
{
foreach (var t in m_DownloadQueue)
t.Dispose();

 
  

break;
}

 
  

var item = m_DownloadQueue.Peek();
switch (item.m_State)
{
case HttpDownloadItem.State.INITIAL:
item.Start();
break;
case HttpDownloadItem.State.COMPLETE:
m_DownloadQueue.Dequeue();
break;
case HttpDownloadItem.State.ERROR:
Loom.QueueOnMainThread(()=> {
if (m_OnError != null)
m_OnError();
});
Debug.LogError("One Download Item Error");
return;
}
}
});
}
}

 

 

你可能感兴趣的:(unity http multithread download)