Unity發(fā)送HTTP請求和文件下載

Unity發(fā)送HTTP請求和文件下載

本類庫封裝基于ulua框架LuaFramework

1. 使用的類庫

HttpWebRequest
HttpWebResponse
LuaFramework

2. 實現(xiàn)了哪些功能

  • 發(fā)送GET、POST請求
  • HTTP請求異步化
  • 支持Lua回調
  • 支持Host,采用Proxy的實現(xiàn)方式
  • 支持將HTTP請求內容保存為文件
  • HTTP下載文件,支持斷電續(xù)傳

3. HTTP實現(xiàn)思路

  • HTTPRequest 發(fā)起HTTP請求,異步回調返回HTTPResponse
    HTTPRequest源碼
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Collections.Generic;
using LuaFramework;
using UnityEngine;
/// <summary>
/// Http請求
/// </summary>
public class HTTPRequest
{
    private string url;
    private int timeout;
    private Action<HTTPResponse> callback;
    private HttpWebRequest request;
    private string method;
    private string contentType;
    private KeyValuePair<string, int> proxy;
    protected int range = -1;
    // post內容
    private StringBuilder postBuilder;
    /// <summary>
    /// 錯誤代碼
    /// </summary>
    public const int ERR_EXCEPTION = -1;
    /// <summary>
    /// 構造函數(shù), 構造GET請求
    /// </summary>
    /// <param name="url">url地址</param>
    /// <param name="timeout">超時時間</param>
    /// <param name="callback">回調函數(shù)</param>
    public HTTPRequest (string url, string method, int timeout, Action<HTTPResponse> callback)
    {
        this.url = url;
        this.timeout = timeout;
        this.callback = callback;
        this.method = method.ToUpper();
    }
    /// <summary>
    /// 設置Post內容
    /// </summary>
    /// <param name="data">內容</param>
    public void SetPostData(string data) {
        if (postBuilder == null) {
            postBuilder = new StringBuilder (data.Length);
        }
        if (postBuilder.Length > 0) {
            postBuilder.Append ("&");
        }
        postBuilder.Append (data);
    }
    /// <summary>
    /// 添加Post內容
    /// </summary>
    /// <param name="key">key值</param>
    /// <param name="value">value值</param>
    public void AddPostData(string key, string value) {
        if (postBuilder == null) {
            postBuilder = new StringBuilder ();
        }
        if (postBuilder.Length > 0) {
            postBuilder.Append ("&");
        }
        postBuilder.Append (key).Append ("=").Append (UrlEncode (value));
    }
    /// <summary>
    /// 設置代理
    /// </summary>
    /// <param name="ip">ip地址</param>
    /// <param name="port">端口號</param>
    public void SetProxy(string ip, int port) {
        this.proxy = new KeyValuePair<string, int> (ip, port);
    }
    /// <summary>
    /// 設置ContentType
    /// </summary>
    /// <value>ContentType value</value>
    public string ContentType {
        set {
            this.contentType = value;
        }
    }
    /// <summary>
    /// 發(fā)動請求
    /// </summary>
    public void Start() {
        Debug.Log ("Handle Http Request Start");
        this.request = WebRequest.Create (url) as HttpWebRequest;
        this.request.Timeout = timeout;
        this.request.Method = method;
        if (this.proxy.Key != null) {
            this.request.Proxy = new WebProxy(this.proxy.Key, this.proxy.Value);
        }
        if (this.contentType != null) {
            this.request.ContentType = this.contentType;
        }
        if (this.range != -1) {
            this.request.AddRange (this.range);
        }
        // POST寫POST內容
        if (method.Equals ("POST")) {
            WritePostData ();
        }
        try {
            AsyncCallback callback = new AsyncCallback (OnResponse);
            this.request.BeginGetResponse (callback, null);
        } catch (Exception e) {
            CallBack (ERR_EXCEPTION, e.Message);
            if (request != null) {
                request.Abort ();
            }
        }
    }
    /// <summary>
    /// 處理讀取Response
    /// </summary>
    /// <param name="result">異步回到result</param>
    protected void OnResponse(IAsyncResult result) {
        //Debug.Log ("Handle Http Response");
        HttpWebResponse response = null;
        try {
            // 獲取Response
            response = request.EndGetResponse (result) as HttpWebResponse;
            if (response.StatusCode == HttpStatusCode.OK) {
                if ("HEAD".Equals(method)) {
                    // HEAD請求
                    long contentLength = response.ContentLength;
                    CallBack((int)response.StatusCode, contentLength + "");
                    return;
                }
                // 讀取請求內容
                Stream responseStream = response.GetResponseStream();
                byte[] buff = new byte[2048];
                MemoryStream ms = new MemoryStream();
                int len = -1;
                while ((len = responseStream.Read(buff, 0, buff.Length)) > 0) {
                    ms.Write(buff, 0, len);
                }
                // 清理操作
                responseStream.Close();
                response.Close();
                request.Abort();
                // 調用回調
                CallBack ((int)response.StatusCode, ms.ToArray());
            } else {
                CallBack ((int)response.StatusCode, "");
            }
        } catch (Exception e) {
            CallBack (ERR_EXCEPTION, e.Message);
            if (request != null) {
                request.Abort ();
            }
            if (response != null) {
                response.Close ();
            }
        }
    }
    /// <summary>
    /// 回調
    /// </summary>
    /// <param name="code">編碼</param>
    /// <param name="content">內容</param>
    private void CallBack(int code, string content) {
        Debug.LogFormat ("Handle Http Callback, code:{0}", code);
        if (callback != null) {
            HTTPResponse response = new HTTPResponse ();
            response.StatusCode = code;
            response.Error = content;
            callback (response);
        }
    }
    /// <summary>
    /// 回調
    /// </summary>
    /// <param name="code">編碼</param>
    /// <param name="content">內容</param>
    private void CallBack(int code, byte[] content) {
        Debug.LogFormat ("Handle Http Callback, code:{0}", code);
        if (callback != null) {
            HTTPResponse response = new HTTPResponse (content);
            response.StatusCode = code;
            callback (response);
        }
    }
    /// <summary>
    /// 寫Post內容
    /// </summary>
    private void WritePostData() {
        if (null == postBuilder || postBuilder.Length <= 0) {
            return;
        }
        byte[] bytes = Encoding.UTF8.GetBytes (postBuilder.ToString ());
        Stream stream = request.GetRequestStream ();
        stream.Write (bytes, 0, bytes.Length);
        stream.Close ();
    }
    /// <summary>
    /// URLEncode
    /// </summary>
    /// <returns>encode value</returns>
    /// <param name="value">要encode的值</param>
    private string UrlEncode(string value) {
        StringBuilder sb = new StringBuilder();
        byte[] byStr = System.Text.Encoding.UTF8.GetBytes(value);
        for (int i = 0; i < byStr.Length; i++)
        {
            sb.Append(@"%" + Convert.ToString(byStr[i], 16));
        }
        return (sb.ToString());
    }
}

HTTPResponse源碼

using System;
using System.IO;
using System.Text;
using UnityEngine;
using LuaFramework;
/// <summary>
/// HTTP返回內容
/// </summary>
public class HTTPResponse
{
    // 狀態(tài)碼
    private int statusCode;
    // 響應字節(jié)
    private byte[] responseBytes;
    // 錯誤內容
    private string error;
    /// <summary>
    /// 默認構造函數(shù)
    /// </summary>
    public HTTPResponse ()
    {
    }
    /// <summary>
    /// 構造函數(shù)
    /// </summary>
    /// <param name="content">響應內容</param>
    public HTTPResponse (byte[] content)
    {
        this.responseBytes = content;
    }
    /// <summary>
    /// 獲取響應內容
    /// </summary>
    /// <returns>響應文本內容</returns>
    public string GetResponseText() {
        if (null == this.responseBytes) {
            return null;
        }
        return Encoding.UTF8.GetString (this.responseBytes);
    }
    /// <summary>
    /// 將響應內容存儲到文件
    /// </summary>
    /// <param name="fileName">文件名稱</param>
    public void SaveResponseToFile(string fileName) {
        if (null == this.responseBytes) {
            return;
        }
        // FIXME 路徑跨平臺問題
        string path = Path.Combine (Application.dataPath +"/StreamingAssets", fileName);
        FileStream fs = new FileStream (path, FileMode.Create);
        BinaryWriter writer = new BinaryWriter (fs);
        writer.Write (this.responseBytes);
        writer.Flush ();
        writer.Close ();
        fs.Close ();
    }
    /// <summary>
    /// 獲取狀態(tài)碼
    /// </summary>
    /// <value>狀態(tài)碼</value>
    public int StatusCode {
        set { 
            this.statusCode = value;
        }
        get {
            return this.statusCode;
        }
    }
    /// <summary>
    /// 獲取錯誤消息
    /// </summary>
    /// <value>錯誤消息</value>
    public string Error {
        set {
            this.error = value;
        }
        get {
            return this.error;
        }
    }
}

NetworkManager中封裝了一個發(fā)送HTTP請求的方法

/// <summary>
/// 創(chuàng)建一個HTTP請求
/// </summary>
/// <param name="url">URL.</param>
/// <param name="callback">Callback</param>
public HTTPRequest CreateHTTPRequest(string url, string method, LuaFunction callback) {
    HTTPRequest client = new HTTPRequest(url, method, 5000, (HTTPResponse response)=>{
   if (null != callback) {
           callbackEvents.Enqueue(() => {
               callback.Call(response);
           });
       }
    });
    return client;
}

Lua中使用范例

    local request = networkMgr:CreateHTTPRequest("http://www.baidu.com", "GET", function(response)
        response:GetResponseText()
    end
    )
    # 支持HOST模式
    #request:SetProxy("42.62.46.224", 80)
    request:Start()

4. 文件下載實現(xiàn)思路

這里主要借鑒了Unity技術博客 - 客戶端斷點續(xù)傳這篇文章的思想
支持了Lua回調

HTTPDownLoad源碼

using System;
using System.Net;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Threading;
using LuaFramework;
using UnityEngine;
/// <summary>
/// 文件下載
/// </summary>
public class HTTPDownLoad
{
    // 下載進度
    public float Progress {
        get;
        private set;
    }
    // 狀態(tài) 0 正在下載 1 下載完成 -1 下載出錯
    public int Status {
        get;
        private set;
    }
    // 錯誤信息
    public string Error {
        get;
        set;
    }
    // 總長度
    public long TotalLength {
        get;
        private set;
    }
    // 保存路徑
    private string savePath;
    // url地址
    private string url;
    // 超時時間
    private int timeout;
    // 子線程
    private Thread thread;
    // 子線程是否停止標志
    private bool isStop;
    /// <summary>
    /// 構造函數(shù)
    /// </summary>
    /// <param name="url">url地址</param>
    /// <param name="timeout">超時時間</param>
    /// <param name="callback">回調函數(shù)</param>
    public HTTPDownLoad (string url, string savePath, int timeout)
    {
        this.savePath = savePath;
        this.url = url;
        this.timeout = timeout;
    }
    /// <summary>
    /// 開啟下載
    /// </summary>
    public void DownLoad() {
        // 開啟線程下載
        thread = new Thread (StartDownLoad);
        thread.IsBackground = true;
        thread.Start ();
    }
    /// <summary>
    /// 開始下載
    /// </summary>
    private void StartDownLoad() {
        try {
            // 構建文件流
            FileStream fs = new FileStream (this.savePath, FileMode.OpenOrCreate, FileAccess.Write);
            // 文件當前長度
            long fileLength = fs.Length;
            // 文件總長度
            TotalLength = GetDownLoadFileSize ();
            Debug.LogFormat("fileLen:{0}", TotalLength);
            if (fileLength < TotalLength) {
                // 沒有下載完成
                fs.Seek (fileLength, SeekOrigin.Begin);
                // 發(fā)送請求開始下載
                HttpWebRequest request = WebRequest.Create (this.url) as HttpWebRequest;
                request.AddRange ((int)fileLength);
                request.Timeout = this.timeout;
                // 讀取文件內容
                Stream stream = request.GetResponse().GetResponseStream();
                if (stream.CanTimeout) {
                    stream.ReadTimeout = this.timeout;
                }
                byte[] buff = new byte[4096];
                int len = -1;
                while ((len = stream.Read (buff, 0, buff.Length)) > 0) {
                    if (isStop) {
                        break;
                    }
                    fs.Write (buff, 0, len);
                    fileLength += len;
                    Progress = fileLength * 1.0f / TotalLength;
                } 
                stream.Close ();
                stream.Dispose ();
            } else {
                Progress = 1;
            }
            fs.Close ();
            fs.Dispose ();
            // 標記下載完成
            if (Progress == 1) {
                Status = 1;
            }
        } catch (Exception e) {
            Error = e.Message;
            Status = -1;
        }
    }
    /// <summary>
    /// 獲取下載的文件大小
    /// </summary>
    /// <returns>文件大小</returns>
    public long GetDownLoadFileSize() {
        HttpWebRequest request = WebRequest.Create (this.url) as HttpWebRequest;
        request.Method = "HEAD";
        request.Timeout = this.timeout;
        HttpWebResponse response = request.GetResponse () as HttpWebResponse;
        return response.ContentLength;
    }
    /// <summary>
    /// 關閉下載
    /// </summary>
    public void Close() {
        this.isStop = true;
    }
}

NetworkManager中封裝了一個文件下載的方法

/// <summary>
/// 下載文件
/// </summary>
/// <param name="url">url</param>
/// <param name="savePath">保存地址</param>
/// <param name="callback">callback</param>
public HTTPDownLoad DownLoadFile(string url, string savePath, LuaFunction callback) {
    // 一次只允許下載一個文件
    if (downFileEvent != null) {
        callback.Call (-1, 0, "其他文件正在下載");
        return null;
    }
    // 下載文件
    HTTPDownLoad download = new HTTPDownLoad (url, savePath, 5000);
    download.DownLoad ();
    // 回調函數(shù)
    downFileEvent = delegate(){
        callback.Call (download.Status, download.Progress, download.Error);
        if (download.Status != 0) {
            downFileEvent = null;
        }
    };
    return download;
}

Lua中使用范例

local path = "/Workspace/Unity/LuaFramework_UGUI/Assets/StreamingAssets/***.zip"
local downloadRequest = networkMgr:DownLoadFile("文件下載url", path, function(status, progress, error)
        print("status:" .. status .. ", progress:" .. progress)
        if error ~= nil then
        print("error:" .. error)
        end
end)

4. 交流與討論

歡迎大家交流和討論O(∩_∩)O~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,623評論 18 399
  • apache下的httpclient工具可大大簡化開發(fā)過程中的點對點通信,本人將以微信多媒體接口為例,展示http...
    劃破的天空閱讀 5,521評論 0 32
  • 一套完整的登陸注冊業(yè)務邏輯 準備部分基礎工具類Basepackage com.jericho.tools;impo...
    JerichoPH閱讀 2,549評論 0 9
  • 小時候,有個人很愛你 長大了,有個人很愛你 你老了,非常愛那個人 從你呱呱墜地的一瞬間,有一個人比你還激動,從那一...
    長不大小屁孩閱讀 353評論 0 1

友情鏈接更多精彩內容