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阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,581评论 18 399
  • apache下的httpclient工具可大大简化开发过程中的点对点通信,本人将以微信多媒体接口为例,展示http...
    划破的天空阅读 5,266评论 0 32
  • 一套完整的登陆注册业务逻辑 准备部分基础工具类Basepackage com.jericho.tools;impo...
    JerichoPH阅读 2,432评论 0 9
  • 小时候,有个人很爱你 长大了,有个人很爱你 你老了,非常爱那个人 从你呱呱坠地的一瞬间,有一个人比你还激动,从那一...
    长不大小屁孩阅读 259评论 0 1