• HttpWebRequest.AddRange 支持long类型


    很久很久以前,在哪个FAT32格式还流行的年代,文件大小普遍还没超过4G的年代,.Net已经出来了。

    而那时候.Net实现的HTTP断点续传协议,还没预料到如此普及(我猜的)。那时候的HttpWebRequest.AddRange 还仅限于 int(Int32)类型。。。

    虽然在.Net 4.0 以后,HttpWebRequest的 AddRange 方法已经默认支持 long类型(Int64)。但是相对于比较旧的版本上,应该如何支持呢?特别是Unity3D 这些N久还在用Mono 2.6(相当于 .Net 3.5)的时候。

    当然网上也有相应的解决方案:

     1 MethodInfo method = typeof(WebHeaderCollection).GetMethod("AddWithoutValidate", BindingFlags.Instance | BindingFlags.NonPublic);
     2 
     3 HttpWebRequest request = (HttpWebRequest)WebRequest.Create ("http://www.example.com/file.exe");
     4 
     5 long start = int32.MaxValue;
     6 long end = int32.MaxValue +  100000;
     7 
     8 string key = "Range";
     9 string val = string.Format ("bytes={0}-{1}", start, end);
    10 
    11 method.Invoke (request.Headers, new object[] { key, val });

    也有一个不需要反射(Reflection)的继承版本,当然这个做法不可行,因为HttpWebRequest.set Headers时候内部是重新构造了WebHeaderCollection 对象,而且并没有将 "Range" 头信息添加进去

     1 //错误版本
     2 public class InheritWebHeaders : WebHeaderCollection
     3 {
     4     /*
     5     define your properties & methods here. -- by Mitchell Chu
     6     */
     7     public void AddHeaderWithoutValidate(
     8         string name
     9         , string value)
    10     {
    11         base.AddWithoutValidate(name, value);
    12     }
    13 }
    14  
    15 // usage:
    16 HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://blog.useasp.net/");
    17 InheritWebHeaders headers = new InheritWebHeaders();
    18 headers.AddHeaderWithoutValidate("Referrer", "http://blog.useasp.net/tag/.net");
    19 // other HTTP Headers
    20 request.Headers = headers;
    21 /// do more here...

    所以 还是只能反射来支持long类型了,使用ILSpy 查看一下 System.dll(2.0.0.0) 中HttpWebRequest.AddRange的实现:

     1 public void AddRange(int from, int to)
     2 {
     3     this.AddRange("bytes", from, to);
     4 }
     5 public void AddRange(int range)
     6 {
     7     this.AddRange("bytes", range);
     8 }
     9 
    10 public void AddRange(string rangeSpecifier, int from, int to)
    11 {
    12     if (rangeSpecifier == null)
    13     {
    14         throw new ArgumentNullException("rangeSpecifier");
    15     }
    16     if (from < 0 || to < 0)
    17     {
    18         throw new ArgumentOutOfRangeException(SR.GetString("net_rangetoosmall"));
    19     }
    20     if (from > to)
    21     {
    22         throw new ArgumentOutOfRangeException(SR.GetString("net_fromto"));
    23     }
    24     if (!WebHeaderCollection.IsValidToken(rangeSpecifier))
    25     {
    26         throw new ArgumentException(SR.GetString("net_nottoken"), "rangeSpecifier");
    27     }
    28     if (!this.AddRange(rangeSpecifier, from.ToString(NumberFormatInfo.InvariantInfo), to.ToString(NumberFormatInfo.InvariantInfo)))
    29     {
    30         throw new InvalidOperationException(SR.GetString("net_rangetype"));
    31     }
    32 }
    33 
    34 public void AddRange(string rangeSpecifier, int range)
    35 {
    36     if (rangeSpecifier == null)
    37     {
    38         throw new ArgumentNullException("rangeSpecifier");
    39     }
    40     if (!WebHeaderCollection.IsValidToken(rangeSpecifier))
    41     {
    42         throw new ArgumentException(SR.GetString("net_nottoken"), "rangeSpecifier");
    43     }
    44     if (!this.AddRange(rangeSpecifier, range.ToString(NumberFormatInfo.InvariantInfo), (range >= 0) ? "" : null))
    45     {
    46         throw new InvalidOperationException(SR.GetString("net_rangetype"));
    47     }
    48 }
    49 
    50 private bool AddRange(string rangeSpecifier, string from, string to)
    51 {
    52     string text = this._HttpRequestHeaders["Range"];
    53     if (text == null || text.Length == 0)
    54     {
    55         text = rangeSpecifier + "=";
    56     }
    57     else
    58     {
    59         if (string.Compare(text.Substring(0, text.IndexOf('=')), rangeSpecifier, StringComparison.OrdinalIgnoreCase) != 0)
    60         {
    61             return false;
    62         }
    63         text = string.Empty;
    64     }
    65     text += from.ToString();
    66     if (to != null)
    67     {
    68         text = text + "-" + to;
    69     }
    70     this._HttpRequestHeaders.SetAddVerified("Range", text);
    71     return true;
    72 }

    由此看出 AddRange 方法最终实现是 AddRange(string rangeSpecifier, string from, string to)

    这样做的好处是有扩展性,哪怕以后文件大到Int128,Int256 也是可以支持的。但是至于为何不公开,应该是为了防止某些程序员乱写FromTo的参数吧,万一写成了非整型类型呢~

    所以,相应地我们可以用反射写一个工具类(网上找的):HttpWebRequest.AddRange support for long values

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Net;
     6 using System.Reflection;
     7 
     8 namespace HttpHelper
     9 {
    10     public static class HttpHelper
    11     {
    12         private static MethodInfo _addRangeMethodInfo = null;
    13         private static void GetMethodInfo()
    14         {
    15             if (_addRangeMethodInfo == null)
    16             {
    17                 Type t = typeof(HttpWebRequest);
    18                 MethodInfo[] methodInfoArray = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
    19                 foreach (MethodInfo mi in methodInfoArray)
    20                     if (mi.Name == "AddRange")
    21                     {
    22                         _addRangeMethodInfo = mi;
    23                         return;
    24                     }
    25                 throw new Exception("HttpWebRequest type is missing the private AddRange method");
    26             }
    27         }
    28 
    29         public static void AddRange(this HttpWebRequest req, long from, long to)
    30         {
    31             GetMethodInfo();
    32 
    33             string rangeSpecifier = "bytes";
    34             string fromString = from.ToString();
    35             string toString = to.ToString();
    36 
    37             object[] args = new object[3];
    38 
    39             args[0] = rangeSpecifier;
    40             args[1] = fromString;
    41             args[2] = toString;
    42 
    43             _addRangeMethodInfo.Invoke(req, args);
    44         }
    45 
    46         public static void AddRange(this HttpWebRequest req, long from)
    47         {
    48             GetMethodInfo();
    49 
    50             string rangeSpecifier = "bytes";
    51             string fromString = from.ToString();
    52             string toString = "";
    53 
    54             object[] args = new object[3];
    55 
    56             args[0] = rangeSpecifier;
    57             args[1] = fromString;
    58             args[2] = toString;
    59 
    60             _addRangeMethodInfo.Invoke(req, args);
    61         }
    62     }
    63 }

    当然事情还没结束,因为我们要支持的是还在使用老旧Mono版本的Unity3D,而Mono的HttpWebRequest内部实现 又和.Net的实现不同,所以上述的方法在U3D上根本不Work。

    照版煮碗,我们先看一下U3D种 System.dll 的实现(如果找不到System.dll,可以先将工程Build一个出来,然后在工程文件内部(Xxx_Data/Managed/System.dll)即可找到。

    我目前使用的是Unity4.6.4(项目需要,目前一直在使用此版本), 找到的System.dll版本为 2.0.5.0

    public void AddRange(int range)
    {
        this.AddRange("bytes", range);
    }
    
    public void AddRange(int from, int to)
    {
        this.AddRange("bytes", from, to);
    }
    
    
    public void AddRange(string rangeSpecifier, int range)
    {
        if (rangeSpecifier == null)
        {
            throw new ArgumentNullException("rangeSpecifier");
        }
        string text = this.webHeaders["Range"];
        if (text == null || text.Length == 0)
        {
            text = rangeSpecifier + "=";
        }
        else
        {
            if (!text.ToLower().StartsWith(rangeSpecifier.ToLower() + "="))
            {
                throw new InvalidOperationException("rangeSpecifier");
            }
            text += ",";
        }
        this.webHeaders.RemoveAndAdd("Range", text + range + "-");
    }
    
    
    public void AddRange(string rangeSpecifier, int from, int to)
    {
        if (rangeSpecifier == null)
        {
            throw new ArgumentNullException("rangeSpecifier");
        }
        if (from < 0 || to < 0 || from > to)
        {
            throw new ArgumentOutOfRangeException();
        }
        string text = this.webHeaders["Range"];
        if (text == null || text.Length == 0)
        {
            text = rangeSpecifier + "=";
        }
        else
        {
            if (!text.ToLower().StartsWith(rangeSpecifier.ToLower() + "="))
            {
                throw new InvalidOperationException("rangeSpecifier");
            }
            text += ",";
        }
        this.webHeaders.RemoveAndAdd("Range", string.Concat(new object[]
        {
            text,
            from,
            "-",
            to
        }));
    }
    
    //顺道看一下 Headers的实现
    
    public override WebHeaderCollection Headers
    {
        get
        {
            return this.webHeaders;
        }
        set
        {
            this.CheckRequestStarted();
            WebHeaderCollection webHeaderCollection = new WebHeaderCollection(true);
            int count = value.Count;
            for (int i = 0; i < count; i++)
            {
                webHeaderCollection.Add(value.GetKey(i), value.Get(i));
            }
            this.webHeaders = webHeaderCollection;
        }
    }

    我们可以针对U3D也做一个版本,但是这样反而觉得比较麻烦。 所以可以先给Helper添加默认的AddRange方法,然后通过外部可以覆盖原来的AddRange方法。

    以下是代码:

     1 using System;
     2 using System.Net;
     3 using System.Reflection;
     4 
     5 namespace MyDownloader.Extension.Common
     6 {
     7     public static class HttpWebRequestHelper
     8     {
     9 
    10         public delegate void AddRangeFromToHandler(HttpWebRequest request, long from, long to);
    11         public delegate void AddRangeFromHandler(HttpWebRequest request, long from);
    12 
    13         static MethodInfo _addRangeMethodInfo = null;
    14         static AddRangeFromHandler addRangeFrom = null;
    15         static AddRangeFromToHandler addRangeFromTo = null;
    16 
    17         static HttpWebRequestHelper()
    18         {
    19             SetAddRange(AddRangeInternal, AddRangeInternal);
    20         }
    21 
    22         static void GetMethodInfo()
    23         {
    24             if (_addRangeMethodInfo != null)
    25             {
    26                 return;
    27             }
    28             Type t = typeof(HttpWebRequest);
    29             MethodInfo[] methodInfoArray = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
    30             foreach (MethodInfo mi in methodInfoArray)
    31             {
    32                 if (mi.Name == "AddRange")
    33                 {
    34                     _addRangeMethodInfo = mi;
    35                     return;
    36                 }
    37             }
    38             throw new NotSupportedException("HttpWebRequest type is missing the private AddRange method");
    39         }
    40 
    41 
    42         //默认调用
    43         internal static void AddRangeInternal(HttpWebRequest req, long from, long to)
    44         {
    45             GetMethodInfo();
    46 
    47             string rangeSpecifier = "bytes";
    48             string fromString = from.ToString();
    49             string toString = to.ToString();
    50 
    51             object[] args = new object[3];
    52 
    53             args[0] = rangeSpecifier;
    54             args[1] = fromString;
    55             args[2] = toString;
    56 
    57             _addRangeMethodInfo.Invoke(req, args);
    58         }
    59 
    60         internal static void AddRangeInternal(HttpWebRequest req, long from)
    61         {
    62             GetMethodInfo();
    63 
    64             string rangeSpecifier = "bytes";
    65             string fromString = from.ToString();
    66             string toString = "";
    67 
    68             object[] args = new object[3];
    69 
    70             args[0] = rangeSpecifier;
    71             args[1] = fromString;
    72             args[2] = toString;
    73 
    74             _addRangeMethodInfo.Invoke(req, args);
    75         }
    76 
    77         public static void SetAddRange(AddRangeFromHandler fromOnly, AddRangeFromToHandler fromTo)
    78         {
    79             if (fromOnly == null || fromTo == null)
    80                 throw new ArgumentNullException();
    81             addRangeFrom = fromOnly;
    82             addRangeFromTo = fromTo;
    83         }
    84 
    85         public static void AddRange(HttpWebRequest request, long from)
    86         {
    87             addRangeFrom(request, from);
    88         }
    89 
    90         public static void AddRange(HttpWebRequest request, long from, long to)
    91         {
    92             addRangeFromTo(request, from, to);
    93         }
    94     }
    95 }

    在U3D中使用(TEST VERSION):

    public class HttpWebRequestHelperUnityTest{
    
        public void TEST()
        {
            var http = new MyDownloader.Extension.Protocols.HttpFtpProtocolExtension();
    
            HttpWebRequestHelper.SetAddRange(AddRange, AddRange);
            DownloadManager.Instance.DownloadEnded += (s, e) => { print(e.Downloader.LocalFile); };
        }
        
    
        public void AddRange(HttpWebRequest req, long range)
        {
            AddRange(req, "bytes", range);
        }
    
    
        public void AddRange(HttpWebRequest req, long from, long to)
        {
            AddRange(req, "bytes", from, to);
        }
    
    
        public void AddRange(HttpWebRequest req, string rangeSpecifier, long range)
        {
            if (rangeSpecifier == null)
            {
                throw new ArgumentNullException("rangeSpecifier");
            }
            string text = req.Headers["Range"];
            if (text == null || text.Length == 0)
            {
                text = rangeSpecifier + "=";
            }
            else
            {
                if (!text.ToLower().StartsWith(rangeSpecifier.ToLower() + "="))
                {
                    throw new InvalidOperationException("rangeSpecifier");
                }
                text += ",";
            }
            HeadersRemoveAndAdd(req.Headers, "Range", text + range + "-");
        }
        
        public void AddRange(HttpWebRequest req, string rangeSpecifier, long from, long to)
        {
            if (rangeSpecifier == null)
            {
                throw new ArgumentNullException("rangeSpecifier");
            }
            if (from < 0 || to < 0 || from > to)
            {
                throw new ArgumentOutOfRangeException();
            }
            string text = req.Headers["Range"];
            if (text == null || text.Length == 0)
            {
                text = rangeSpecifier + "=";
            }
            else
            {
                if (!text.ToLower().StartsWith(rangeSpecifier.ToLower() + "="))
                {
                    throw new InvalidOperationException("rangeSpecifier");
                }
                text += ",";
            }
            HeadersRemoveAndAdd(req.Headers, "Range", string.Concat(new object[]
            {
                    text,
                    from,
                    "-",
                    to
            }));
        }
    
        static void HeadersRemoveAndAdd(WebHeaderCollection headers, string name, string value)
        {
            GetMethodInfo();
            removeAndAddRangeMethodInfo.Invoke(headers, new object[] { name, value });
        }
    
        static MethodInfo removeAndAddRangeMethodInfo = null;
        static void GetMethodInfo()
        {
            if (removeAndAddRangeMethodInfo != null)
                return;
    
            Type t = typeof(WebHeaderCollection);
            MethodInfo[] methodInfoArray = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
            foreach (MethodInfo mi in methodInfoArray)
            {
                if (mi.Name == "RemoveAndAdd")
                {
                    removeAndAddRangeMethodInfo = mi;
                    return;
                }
            }
            throw new NotSupportedException("WebHeaderCollection type is missing the private RemoveAndAdd method");
        }
    }
  • 相关阅读:
    android开发学习4
    macOS login: Could not determine audit condition 问题解决
    java代码编程规范
    学习进度报告(二)
    学习进度报告(一)
    android开发笔记
    数组
    软件工程第二周开课博客
    第一周学习总结
    用户体验评价
  • 原文地址:https://www.cnblogs.com/godzza/p/6860454.html
Copyright © 2020-2023  润新知