Unity發送HTTP請求和文件下載

Unity發送HTTP請求和文件下載

本類庫封裝基于ulua框架LuaFramework

1. 使用的類庫

HttpWebRequest
HttpWebResponse
LuaFramework

2. 實現了哪些功能

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

3. HTTP實現思路

  • HTTPRequest 發起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>
    /// 構造函數, 構造GET請求
    /// </summary>
    /// <param name="url">url地址</param>
    /// <param name="timeout">超時時間</param>
    /// <param name="callback">回調函數</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>
    /// 發動請求
    /// </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
{
    // 狀態碼
    private int statusCode;
    // 響應字節
    private byte[] responseBytes;
    // 錯誤內容
    private string error;
    /// <summary>
    /// 默認構造函數
    /// </summary>
    public HTTPResponse ()
    {
    }
    /// <summary>
    /// 構造函數
    /// </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>
    /// 獲取狀態碼
    /// </summary>
    /// <value>狀態碼</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中封裝了一個發送HTTP請求的方法

/// <summary>
/// 創建一個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. 文件下載實現思路

這里主要借鑒了Unity技術博客 - 客戶端斷點續傳這篇文章的思想
支持了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;
    }
    // 狀態 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>
    /// 構造函數
    /// </summary>
    /// <param name="url">url地址</param>
    /// <param name="timeout">超時時間</param>
    /// <param name="callback">回調函數</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);
                // 發送請求開始下載
                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 ();
    // 回調函數
    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~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內容

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