关于HttpWebRequest.AddRange()方法的参数

关于HttpWebRequest.AddRange()方法的参数

2009-02-06 21:14:18|  分类: DotNET(c#) 阅读563 评论3   字号: 订阅

先讲一个故事吧!

我:“Server,请告诉我那个文件有多大?”(当你和Server对话时必须很有礼貌)

Server:“好吧,孩子,那是一个相当大的文件,6个G。你知道的,我很聪明,那就是为什么我是Server你是Client,你可以请求这个文件的一部分。告诉我你想要文件的哪一部分,随你从什么地方开始到什么地方结束,我会把那部分文件给你发过去。”

我:“你看,我已经下载了那个文件的前5个G,让我看看...,嗯...6-5...,怎么计算才最简单呢?嗯...我想我正好需要最后一个G。”

我的程序:webRequest.Headers.Add("range", 5 Gb)

WebRequest:“Oh,不是这样的,你不可以这样,你要用AddRange方法,我会帮助你做好的。”

我:“好吧,我再来...”

我的程序:webRequest.AddRange(from)

编译器:“Oh,你个小笨蛋!你想这个Int64的变量试试吗?我会看着你...”

我:“什么?!WebRequest?难道你不把一个Int64变量当做一个合法的值吗?”

WebRequest:“事实上...我恐怕是这样的。你不得不一次下载完这个大文件...嘿!但是你可以等到.net 4.5,你可能用一些更好用的方法,比如说P2P、增强的设计模式、DTC或其他更多的...”

我:“OK!听起来不错!但是...现在,我还是想用一些现成的办法!难道我还要自已新实现一个吗?”

WebRequest:“好的,但我还是恐怕是的,但你可以首先在一些论坛问一下,我非常确信你不是第一个遇到这个问题的人。我记得我爷爷,WinINet,或多或少的也有这个问题。这是个遗传...或者被设计,就象我们经常说的。”

我:“...有点象是旧事重提了...”

原文请看:http://objectmix.com/dotnet/252325-httpwebrequest-inconsistency-8-a.html

解决办法请继续看。

WebRequest.ContentLength属性是Int64型的,但是AddRange方法只接受Int32型的参数,所在我们在分段下载大于2个G的文件时,在大于(Int32.MaxValue)的地方时,我们就无法实现分段下载了,意味着大于2GB之后的文件必须用一个线程一次下完,否则文件大于2GB的部分我们无法下载。

我也反编过HttpWebRequest,其中有一个私有方法,AddRange(string rangeSpecifier,string from,string to)。但我忘了可以用反射来得到这个方法并调用之,看了上篇帖子后才恍然大悟,其中提到了两种方法,一种是利用反射,一种是利用动态写入一个新方法来实现。
两段代码如下:
利用反射:

Type t = typeof(HttpWebRequest);
object[] args = new object[3];
req = (HttpWebRequest)HttpWebRequest.Create(txtURL.Text);
args[0] = "bytes";
args[1]=start.ToString();
args[2]=end.ToString();
MethodInfo[] mi = t.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
for (MethodI = 0; MethodI < mi.Length; MethodI++)
{
       if (mi[MethodI].Name == "AddRange") break;
}
mi[MethodI].Invoke(req, args);

动态方法:

public delegate void _AddRange(HttpWebRequest httpWebRequest, string rangeSpecifier, string from, string to);
static public _AddRange delegateAddRange;

static AddRangeCodeGenerator()
{
     Type stringType = typeof(string);
     DynamicMethod dynamicMethod = new DynamicMethod("AddRange", typeof(void), new Type[] {
                typeof (HttpWebRequest), stringType, stringType, stringType },
                typeof(AddRangeCodeGenerator), true);
     ILGenerator ilGenerator = dynamicMethod.GetILGenerator();

     ilGenerator.Emit(OpCodes.Ldarg_0);
     ilGenerator.Emit(OpCodes.Ldarg_1);
     ilGenerator.Emit(OpCodes.Ldarg_2);
     ilGenerator.Emit(OpCodes.Ldarg_3);
     ilGenerator.Emit(OpCodes.Call, typeof(System.Net.HttpWebRequest).GetMethod("AddRange", 
                BindingFlags.Instance | BindingFlags.NonPublic));
     ilGenerator.Emit(OpCodes.Pop);
     ilGenerator.Emit(OpCodes.Ret);

     delegateAddRange = dynamicMethod.CreateDelegate(typeof(_AddRange)) as _AddRange;
}

 

AddRange的实现方法如下:

private bool AddRange(string rangeSpecifier, string from, string to)
{
    string str = this._HttpRequestHeaders["Range"];
    if ((str == null) || (str.Length == 0))
    {
        str = rangeSpecifier + "=";
    }
    else
    {
        if (string.Compare(str.Substring(0, str.IndexOf('=')), rangeSpecifier, 5) != 0)
        {
            return false;
        }
        str = string.Empty;
    }
    str = str + from.ToString();
    if (to != null)
    {
        str = str + "-" + to;
    }
    this._HttpRequestHeaders.SetAddVerified("Range", str);
    return true;
}

这个方法,实现
设置HTTP 的Range 标头。具体规范参考HTTP1.1协议规范Header Field Definitions

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

rangeSpecifier:指的是规范中的bytes-unit,一般是bytes
比如:从第1024字节开始下载,则请求报文如下

Range:bytes=1024-

一般用在断点续传和多线程下载。一些多线程下载的软件你可以看到分块,每个分块从不同的位置下载

 

 

你可能感兴趣的:(设计模式,多线程,server,object,String,编译器)