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

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

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

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

 MethodInfo method = typeof(WebHeaderCollection).GetMethod("AddWithoutValidate", BindingFlags.Instance | BindingFlags.NonPublic);

 HttpWebRequest request = (HttpWebRequest)WebRequest.Create ("http://www.example.com/file.exe");

 long start = int32.MaxValue;
long end = int32.MaxValue + ; string key = "Range";
string val = string.Format ("bytes={0}-{1}", start, end); method.Invoke (request.Headers, new object[] { key, val });

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

 //错误版本
public class InheritWebHeaders : WebHeaderCollection
{
/*
define your properties & methods here. -- by Mitchell Chu
*/
public void AddHeaderWithoutValidate(
string name
, string value)
{
base.AddWithoutValidate(name, value);
}
} // usage:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://blog.useasp.net/");
InheritWebHeaders headers = new InheritWebHeaders();
headers.AddHeaderWithoutValidate("Referrer", "http://blog.useasp.net/tag/.net");
// other HTTP Headers
request.Headers = headers;
/// do more here...

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

 public void AddRange(int from, int to)
{
this.AddRange("bytes", from, to);
}
public void AddRange(int range)
{
this.AddRange("bytes", range);
} public void AddRange(string rangeSpecifier, int from, int to)
{
if (rangeSpecifier == null)
{
throw new ArgumentNullException("rangeSpecifier");
}
if (from < || to < )
{
throw new ArgumentOutOfRangeException(SR.GetString("net_rangetoosmall"));
}
if (from > to)
{
throw new ArgumentOutOfRangeException(SR.GetString("net_fromto"));
}
if (!WebHeaderCollection.IsValidToken(rangeSpecifier))
{
throw new ArgumentException(SR.GetString("net_nottoken"), "rangeSpecifier");
}
if (!this.AddRange(rangeSpecifier, from.ToString(NumberFormatInfo.InvariantInfo), to.ToString(NumberFormatInfo.InvariantInfo)))
{
throw new InvalidOperationException(SR.GetString("net_rangetype"));
}
} public void AddRange(string rangeSpecifier, int range)
{
if (rangeSpecifier == null)
{
throw new ArgumentNullException("rangeSpecifier");
}
if (!WebHeaderCollection.IsValidToken(rangeSpecifier))
{
throw new ArgumentException(SR.GetString("net_nottoken"), "rangeSpecifier");
}
if (!this.AddRange(rangeSpecifier, range.ToString(NumberFormatInfo.InvariantInfo), (range >= ) ? "" : null))
{
throw new InvalidOperationException(SR.GetString("net_rangetype"));
}
} private bool AddRange(string rangeSpecifier, string from, string to)
{
string text = this._HttpRequestHeaders["Range"];
if (text == null || text.Length == )
{
text = rangeSpecifier + "=";
}
else
{
if (string.Compare(text.Substring(, text.IndexOf('=')), rangeSpecifier, StringComparison.OrdinalIgnoreCase) != )
{
return false;
}
text = string.Empty;
}
text += from.ToString();
if (to != null)
{
text = text + "-" + to;
}
this._HttpRequestHeaders.SetAddVerified("Range", text);
return true;
}

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

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

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

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Reflection; namespace HttpHelper
{
public static class HttpHelper
{
private static MethodInfo _addRangeMethodInfo = null;
private static void GetMethodInfo()
{
if (_addRangeMethodInfo == null)
{
Type t = typeof(HttpWebRequest);
MethodInfo[] methodInfoArray = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (MethodInfo mi in methodInfoArray)
if (mi.Name == "AddRange")
{
_addRangeMethodInfo = mi;
return;
}
throw new Exception("HttpWebRequest type is missing the private AddRange method");
}
} public static void AddRange(this HttpWebRequest req, long from, long to)
{
GetMethodInfo(); string rangeSpecifier = "bytes";
string fromString = from.ToString();
string toString = to.ToString(); object[] args = new object[]; args[] = rangeSpecifier;
args[] = fromString;
args[] = toString; _addRangeMethodInfo.Invoke(req, args);
} public static void AddRange(this HttpWebRequest req, long from)
{
GetMethodInfo(); string rangeSpecifier = "bytes";
string fromString = from.ToString();
string toString = ""; object[] args = new object[]; args[] = rangeSpecifier;
args[] = fromString;
args[] = toString; _addRangeMethodInfo.Invoke(req, args);
}
}
}

当然事情还没结束,因为我们要支持的是还在使用老旧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 == )
{
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 < || to < || from > to)
{
throw new ArgumentOutOfRangeException();
}
string text = this.webHeaders["Range"];
if (text == null || text.Length == )
{
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 = ; i < count; i++)
{
webHeaderCollection.Add(value.GetKey(i), value.Get(i));
}
this.webHeaders = webHeaderCollection;
}
}

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

以下是代码:

 using System;
using System.Net;
using System.Reflection; namespace MyDownloader.Extension.Common
{
public static class HttpWebRequestHelper
{ public delegate void AddRangeFromToHandler(HttpWebRequest request, long from, long to);
public delegate void AddRangeFromHandler(HttpWebRequest request, long from); static MethodInfo _addRangeMethodInfo = null;
static AddRangeFromHandler addRangeFrom = null;
static AddRangeFromToHandler addRangeFromTo = null; static HttpWebRequestHelper()
{
SetAddRange(AddRangeInternal, AddRangeInternal);
} static void GetMethodInfo()
{
if (_addRangeMethodInfo != null)
{
return;
}
Type t = typeof(HttpWebRequest);
MethodInfo[] methodInfoArray = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (MethodInfo mi in methodInfoArray)
{
if (mi.Name == "AddRange")
{
_addRangeMethodInfo = mi;
return;
}
}
throw new NotSupportedException("HttpWebRequest type is missing the private AddRange method");
} //默认调用
internal static void AddRangeInternal(HttpWebRequest req, long from, long to)
{
GetMethodInfo(); string rangeSpecifier = "bytes";
string fromString = from.ToString();
string toString = to.ToString(); object[] args = new object[]; args[] = rangeSpecifier;
args[] = fromString;
args[] = toString; _addRangeMethodInfo.Invoke(req, args);
} internal static void AddRangeInternal(HttpWebRequest req, long from)
{
GetMethodInfo(); string rangeSpecifier = "bytes";
string fromString = from.ToString();
string toString = ""; object[] args = new object[]; args[] = rangeSpecifier;
args[] = fromString;
args[] = toString; _addRangeMethodInfo.Invoke(req, args);
} public static void SetAddRange(AddRangeFromHandler fromOnly, AddRangeFromToHandler fromTo)
{
if (fromOnly == null || fromTo == null)
throw new ArgumentNullException();
addRangeFrom = fromOnly;
addRangeFromTo = fromTo;
} public static void AddRange(HttpWebRequest request, long from)
{
addRangeFrom(request, from);
} public static void AddRange(HttpWebRequest request, long from, long to)
{
addRangeFromTo(request, from, to);
}
}
}

在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 == )
{
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 < || to < || from > to)
{
throw new ArgumentOutOfRangeException();
}
string text = req.Headers["Range"];
if (text == null || text.Length == )
{
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");
}
}

HttpWebRequest.AddRange 支持long类型的更多相关文章

  1. 实体类的枚举属性--原来支持枚举类型这么简单,没有EF5.0也可以

    通常,我们都是在业务层和界面层使用枚举类型,这能够为我们编程带来便利,但在数据访问层,不使用枚举类型,因为很多数据库都不支持,比如我们现在用的SqlServer2008就不支持枚举类型的列,用的时候也 ...

  2. 让Xcode的控制台支持LLDB类型的打印

    这个技巧个人认为非常有用 当Xcode在断点调试的时候,在控制台中输入 po self.view.frame 类似这样的命令会挂掉,不信可以亲自去试试(Xcode7 以后支持LLDB类型的打印) 那么 ...

  3. EF支持复杂类型的实现

    本节,将介绍如何手动构造复杂类型(ComplexType)以及复杂类型的简单操作.通常,复杂类型是指那些由几个简单的类型组合而成的类型.比如:一张Customer表,其中有FristName和Last ...

  4. Entity Framework 学习中级篇1—EF支持复杂类型的实现

    本节,将介绍如何手动构造复杂类型(ComplexType)以及复杂类型的简单操作. 通常,复杂类型是指那些由几个简单的类型组合而成的类型.比如:一张Customer表,其中有FristName和Las ...

  5. python操作Redis安装、支持存储类型、普通连接、连接池

    一.python操作redis安装和支持存储类型 安装redis模块 pip3 install redis 二.Python操作Redis之普通连接 redis-py提供两个类Redis和Strict ...

  6. 游戏开发中IIS常见支持MIME类型文件解析

    游戏开发中IIS常见支持MIME类型文件解析 .apkapplication/vnd.android .ipaapplication/vnd.iphone .csbapplication/octet- ...

  7. json序列化时定制支持datetime类型,和到中文让他保留中文形式

    json序列化时,可以处理的数据类型有哪些?如何定制支持datetime类型 自定义时间序列化转换器 import json from json import JSONEncoder from dat ...

  8. 【mysql】字段支持JSON类型

    mysql从5.7开始已经支持JSON类型的字段. 支持的操作:添加,修改,置空,子key添加,子key重置,子key删除,通过子key查找等. 但是这里和普通字段的修改和查找不同,涉及到一些JSON ...

  9. EF学习笔记-2 EF之支持复杂类型的实现

    使用过.NET的小伙伴们知道,在我们的实体模型中,除了一些简单模型外,还有一些复杂类型,如几个简单的类型组合而成的类型:而EF除了在实现基本的增删改查之外,也支持复杂类型的实现. 那么如何手动构造复杂 ...

随机推荐

  1. Oracle降低高水位先(转载)

    Oracle  降低高水位线的方法 高水位(HIGH WARTER MARK,HWM)好比水库中储水的水位,用于描述数据库中段的扩展方式.高水位对全表扫描方式有着至关重要的影响.当使用DELETE删除 ...

  2. mysql-day1

    -- 数据库的操作 -- 链接数据库 mysql -uroot -p mysql -uroot -pmysql -- 退出数据库 exit/quit/ctrl+d -- sql语句最后需要有分号;结尾 ...

  3. Go 语言 map (映射)

    1.Go 语言中 map 的定义及初始化: map[Key_Type]Value_Type scence := make(map[string]int) 2.Go 语言的遍历: scene := ma ...

  4. 每日一练之大整数加法(P1255 数楼梯)

    走楼梯走一步还是两步的问题其实就是斐波那契数列(F(n)=F(n-1)+F(n-2),而在int型范围内存在45个相异的数,题干说明楼梯总数可以为5000,则考虑使用字符串进行存储.当两个数相加产生进 ...

  5. Java环境变量PATH和CLASSPATH

    Java开发中常用到环境变量的配置,下面简单介绍下Java中经常配置的环境变量:PATH和CLASSPATH. 1.PATH环境变量 1.1 作用简介 安装完JDK(Java Development ...

  6. vue中v-cloak解决刷新或者加载出现闪烁(显示变量)

    在使用vue绑定数据的时候,渲染页面时会出现变量闪烁,例如 <div class="#app"> <p>{{value.name}}</p> & ...

  7. el-input的color修改无效问题

    相信很多前端初学者跟我一样也遇到过el-input的color修改无效问题 如下图:我想把el-input里面的文字改成蓝色,但是使用总是失败 修改方法:打开调试界面,找到el-input对应的sty ...

  8. ofstream文件输出流把二进制数据写入文件

    #include <fstream> #include <sstream> using namespace std; //在实际应用中,根据需要的不同,选择不同的类来定义:如果 ...

  9. 安装mysql数据库出现错误"系统找不到指定文件"

    http://blog.csdn.net/Marvel__Dead/article/details/63262641?locationNum=4&fps=1

  10. rsa加密算法及js的JSEncrypt实现前端加密

    最近的项目中用到了rsa加密算法,在实现了相关功能之后,我去了解了一下rsa相关原理,于是就写了这篇博客啦. 首先介绍一下什么是rsa加密算法: 作为非对称加密算法的老大,rsa号称是地球上最安全的加 ...