2010-12-01 70 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