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除了在实现基本的增删改查之外,也支持复杂类型的实现. 那么如何手动构造复杂 ...
随机推荐
- JHipster简介
JHipster简介 JHipster或者称Java Hipster,是一个应用代码产生器,能够创建Spring Boot + AngularJS的应用.开源项目地址:JHipster/Github. ...
- cept源代码目录结构详解_知识树(转)
1 简介该代码架构基于版本10.0.5整理,先整理根目录里的代码,再整理出src目录的架构. 2 代码架构2.1 Ceph源码根目录Ceph的根目录下包含了一些文件夹和若干编译.代码格式相关的文件. ...
- Jsp页面输入中文,MYSQL数据库乱码???问题
首先,先看一下自己mysql数据库的编码格式 其次,cmd模式下执行命令set names gbk 最后,更改my.ini文件文件参数为gbk 那为什么会产生乱码问题呢? 原因有以下几种: 一.项目编 ...
- docker 启动报错 WARNING: IPv4 forwarding is disabled. Networking will not work.
解决办法: # vi /etc/sysctl.conf 或者 # vi /usr/lib/sysctl.d/00-system.conf 添加如下代码: net.ipv4.ip_forward ...
- [原创] debian 9.3 搭建Jira+Confluence+Bitbucket项目管理工具(四) -- 安装bitbucket 5.7.0
[原创] debian 9.3 搭建Jira+Confluence+Bitbucket项目管理工具(四) -- 安装bitbucket 5.7.0 安装Bitbucket的教程, 网上能找见的不多, ...
- 最大矩阵(简单DP)
见题: 很水的一题,数据范围太小,前缀和加爆搜就行. #include<bits/stdc++.h> using namespace std; ; ,m,n,sum[maxn][maxn] ...
- docker原理(转)
可能是把Docker的概念讲的最清楚的一篇文章 [编者的话]本文只是对Docker的概念做了较为详细的介绍,并不涉及一些像Docker环境的安装以及Docker的一些常见操作和命令. Docker是世 ...
- File重要获取功能
返回值全是数组 String[] list() 返回当前路径下所有的文件和文件夹名称 注:只有指向文件夹的File对象才可以调此方法,如果只是文件则报错 File f = new File(" ...
- Jenkins Maven Selenium TestNG踩坑记
1)Maven TestNG什么的都配置好了,在本地用eclipse->Run AS->MAVEN INSTALL运行正常 2) Jenkins安装了插件,也建立了MAVEN项目.MAVE ...
- PDO查询语句结果中文乱码
PDO::MYSQL_ATTR_INIT_COMMAND (整数)连接到MySQL服务器时执行的命令.重新连接时会自动重新执行.请注意,此常量只能driver_options 在构建新的数据库句柄时在 ...