2010-12-01 30 views

回答

4

你需要寻找在Request对象是这样的HttpContext传递给ProcessRequest方法的一部分Range头。 HttpRequest类中没有Range属性,因此您必须查看Headers。如果有一个Range它会是这样的形式:

Range: bytes=<start>-<end>

<start><end>是整数。例如,如果某人从文件的中间想64K:

Range: bytes=32768-98304

你必须分析文本转换为数字和相应的处理。

+0

很好的回答。为了后代,一些示例代码会很好。一旦我实现它,我希望你不介意我是否将它注入到答案中。 – Larsenal 2010-12-02 01:42:14

3

请注意,Range标头语法还允许诸如“0-500,100-1500”(多个范围)和“-500”(最后500个字节)之类的内容。有关血腥详细信息,请参阅RFC 2616,这些内容太长而无法引用。

0

基于在@ brent-keller评论中链接到上面的博客帖子,该博客帖子又引用CodePlex entry -I提出了下面的编辑。它使用FDM进行测试(可用here)。 MultiRange请求不支持(还)。否需要Web.config条目。

CodePlex中的原始方法包含错误 - Accept-Ranges标头的值应该是bytes,而不是要返回的字节范围。这属于Content-Range标题。下载仍然有效,但如果你犯了这个错误,你将不会得到byte serving

为了简洁和易读,此修改后的版本进行了重构。它还有一个好处,即返回的文件不一定与实际的URL绑定在一起 - 事实上,可以直接从浏览器调用处理程序,如果需要的话可以使用查询字符串参数。这使动态文件/数据创建和响应成为可能。

希望有人可以用它做一些好事。

HTTP处理程序

Public Class Upload 
    Implements IHttpHandler 

    Public Sub ProcessRequest(Context As HttpContext) Implements IHttpHandler.ProcessRequest 
    Dim oFile As FileInfo 

    oFile = New FileInfo(Context.Server.MapPath("~/0HCJ0LE.zip")) 

    Me.UploadRange(Context, oFile) 
    End Sub 



    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable 
    Get 
     Return False 
    End Get 
    End Property 



    Private Sub UploadRange(Context As HttpContext, File As FileInfo) 
    Dim oResponse As Response 
    Dim oRequest As Request 

    Dim _ 
     nOffset, 
     nLength As Long 

    Using oReader As New StreamReader(File.FullName) 
     Context.Response.AddHeader("Accept-Ranges", "bytes") 

     oResponse = New Response(oReader) 
     oRequest = New Request(oResponse, Context) 

     If oRequest.HasRange Then 
     If oRequest.IsMultiRange Then 
      ' At the moment we only support single ranges.' 
      '   * Multiple range support requires some more work' 
      '   * to comply with the specifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2' 
      '   *' 
      '   * Multirange content must be sent with multipart/byteranges mediatype,' 
      '   * (mediatype = mimetype)' 
      '   * as well as a boundary header to indicate the various chunks of data.' 
      ' '   
      ' (?) Shoud this be issued here, or should the first' 
      ' range be used? Or should the header be ignored and' 
      ' we output the whole content?' 
      Me.ThrowBadRange(Context, oResponse) 
     Else 
      If oRequest.IsBadRange Then 
      Me.ThrowBadRange(Context, oResponse) 
      Else 
      Context.Response.StatusCode = 206 

      oResponse.Start = oRequest.Start 
      oResponse.End = oRequest.End 

      nOffset = oReader.BaseStream.Seek(oResponse.Start, SeekOrigin.Begin) 
      nLength = oResponse.End - oResponse.Start + 1 
      End If 
     End If 
     Else 
     nOffset = 0 
     nLength = oResponse.Size 
     End If 
    End Using 

    Context.Response.ContentType = MediaTypeNames.Application.Zip 
    Context.Response.AddHeader("Content-Disposition", $"attachment; filename={File.Name}") 
    Context.Response.AddHeader("Content-Length", nLength) 
    Context.Response.AddHeader(oResponse.HeaderName, oResponse.HeaderValue) 
    Context.Response.WriteFile(File.FullName, nOffset, nLength) 
    Context.Response.End() 
    End Sub 



    Private Sub ThrowBadRange(Context As HttpContext, Response As Response) 
    Context.Response.AddHeader(Response.HeaderName, Response.HeaderValue) 
    Throw New HttpException(416, "Requested range not satisfiable") 
    End Sub 
End Class 

范围请求

Friend NotInheritable Class Request 
    Public Sub New(Response As Response, Context As HttpContext) 
    Me.Response = Response 
    Me.Context = Context 
    End Sub 



    Public ReadOnly Property Start As Long 
    Get 
     If Me.Range(0) = String.Empty Then 
     Start = Me.Response.Size - Me.Range(1) 
     Else 
     Start = Me.Range(0) 
     End If 
    End Get 
    End Property 



    Public ReadOnly Property [End] As Long 
    Get 
     If Me.Range(0) = String.Empty Then 
     [End] = Me.Response.End 
     Else 
     If Long.TryParse(Me.Range(1), 0) Then 
      [End] = Me.Range(1) 
     Else 
      [End] = Me.Response.Size 
     End If 
     End If 

     [End] = Math.Min(Me.Response.End, [End]) 
    End Get 
    End Property 



    Public ReadOnly Property HasRange As Boolean 
    Get 
     Return String.IsNullOrEmpty(Me.Context.Request.ServerVariables(HTTP_RANGE)) = False 
    End Get 
    End Property 



    Public ReadOnly Property IsMultiRange As Boolean 
    Get 
     Return Me.Context.Request.ServerVariables(HTTP_RANGE).Contains(",") 
    End Get 
    End Property 



    Public ReadOnly Property IsBadRange As Boolean 
    Get 
     Return Me.Start > Me.End OrElse Me.Start > Me.Response.Size - 1 OrElse Me.End >= Me.Response.Size 
    End Get 
    End Property 



    Private ReadOnly Property Range As List(Of String) 
    Get 
     Return Me.Context.Request.ServerVariables(HTTP_RANGE).Split("=")(1).Split("-").ToList 
    End Get 
    End Property 



    Private ReadOnly Response As Response 
    Private ReadOnly Context As HttpContext 

    Private Const HTTP_RANGE As String = "HTTP_RANGE" 
End Class 

范围响应

Friend NotInheritable Class Response 
    Public Sub New(Reader As StreamReader) 
    _Size = Reader.BaseStream.Length 
    Me.End = Me.Size - 1 
    End Sub 



    Public Property Start As Long 
    Public Property [End] As Long 
    Public ReadOnly Property Size As Long 



    Public ReadOnly Property HeaderName As String 
    Get 
     Return "Content-Range" 
    End Get 
    End Property 



    Public ReadOnly Property HeaderValue() As String 
    Get 
     Return $"bytes {Me.Start}-{Me.End}/{Me.Size}" 
    End Get 
    End Property 
End Class