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)的时候。
当然网上也有相应的解决方案:
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类型的更多相关文章
- 实体类的枚举属性--原来支持枚举类型这么简单,没有EF5.0也可以
通常,我们都是在业务层和界面层使用枚举类型,这能够为我们编程带来便利,但在数据访问层,不使用枚举类型,因为很多数据库都不支持,比如我们现在用的SqlServer2008就不支持枚举类型的列,用的时候也 ...
- 让Xcode的控制台支持LLDB类型的打印
这个技巧个人认为非常有用 当Xcode在断点调试的时候,在控制台中输入 po self.view.frame 类似这样的命令会挂掉,不信可以亲自去试试(Xcode7 以后支持LLDB类型的打印) 那么 ...
- EF支持复杂类型的实现
本节,将介绍如何手动构造复杂类型(ComplexType)以及复杂类型的简单操作.通常,复杂类型是指那些由几个简单的类型组合而成的类型.比如:一张Customer表,其中有FristName和Last ...
- Entity Framework 学习中级篇1—EF支持复杂类型的实现
本节,将介绍如何手动构造复杂类型(ComplexType)以及复杂类型的简单操作. 通常,复杂类型是指那些由几个简单的类型组合而成的类型.比如:一张Customer表,其中有FristName和Las ...
- python操作Redis安装、支持存储类型、普通连接、连接池
一.python操作redis安装和支持存储类型 安装redis模块 pip3 install redis 二.Python操作Redis之普通连接 redis-py提供两个类Redis和Strict ...
- 游戏开发中IIS常见支持MIME类型文件解析
游戏开发中IIS常见支持MIME类型文件解析 .apkapplication/vnd.android .ipaapplication/vnd.iphone .csbapplication/octet- ...
- json序列化时定制支持datetime类型,和到中文让他保留中文形式
json序列化时,可以处理的数据类型有哪些?如何定制支持datetime类型 自定义时间序列化转换器 import json from json import JSONEncoder from dat ...
- 【mysql】字段支持JSON类型
mysql从5.7开始已经支持JSON类型的字段. 支持的操作:添加,修改,置空,子key添加,子key重置,子key删除,通过子key查找等. 但是这里和普通字段的修改和查找不同,涉及到一些JSON ...
- EF学习笔记-2 EF之支持复杂类型的实现
使用过.NET的小伙伴们知道,在我们的实体模型中,除了一些简单模型外,还有一些复杂类型,如几个简单的类型组合而成的类型:而EF除了在实现基本的增删改查之外,也支持复杂类型的实现. 那么如何手动构造复杂 ...
随机推荐
- Jmeter分布式部署- linux
https://www.cnblogs.com/beginner-boy/p/7836276.html https://www.cnblogs.com/wuhenyan/p/6419368.html ...
- 阿里云 RDS for MySQL 物理备份文件恢复到自建数据库
想把阿里云的Mysql 生成的RAS 文件.tar文件 恢复到本地自建mysql, 遇到的坑.希望帮助大家 阿里云提供的地址 https://help.aliyun.com/knowledge_det ...
- CAtia_打开提示:许可证过期怎么办
CAtia_许可证过期怎么办:进计算机管理,点开服务和应用程序,点服务,找到DS License Server,在启动此服务的地方点启动,从而开启DS License Server.
- js:一些基础
JavaScript 基础(一) JavaScript的引入方式 直接编写 <!DOCTYPE html> <html lang="en"> <h ...
- Chrome添加Axure RP插件
之前一直用 Firefox 浏览器浏览原型文件,一直用不惯,而且用 Firefox 的唯一目的就是看原型.其他都是用 Chrome 浏览器,来回切换,各种麻烦,然后下定决心解决 Chrome 浏览器无 ...
- Linux命令:内建命令
本文对内建命令进行归类,便于学习和记忆. 分类 内建命令 同义词 功能相反命令 定义&声明类 alias unalias declare typeset local reado ...
- Java学习--变量
参考 http://www.runoob.com/java/java-variable-types.html java变量使用前 java中的所有变量在使用前必须声明并且初始化 类变量和实例变量会默 ...
- Eclipse使用Maven创建Web时错误:Could not resolve archetype
请检查maven的setting 是否有问题.window->Perfenence->maven->User Settings里 看 Gloal Setting和User Setti ...
- python待学习内容
1.Python中不尽如人意的断言Assertion https://www.cnblogs.com/cicaday/p/python-assert.html 2.Python中的反转字符串问题 ht ...
- C# 加载静态资源问题
加载的格式是这样: