【问题标题】:Detecting Byte-Range requests in .NET HttpHandler在 .NET HttpHandler 中检测字节范围请求
【发布时间】:2011-05-18 19:38:45
【问题描述】:

我有一个 HttpHandler,它将对传入的请求进行一些检查,并在某些情况下执行一些功能。需要检查的条件之一是请求是否为byte-range request。这是怎么做到的?

【问题讨论】:

    标签: .net http httphandler


    【解决方案1】:

    您需要在 Request 对象中查找 Range 标头,该标头是传递给您的 ProcessRequest 方法的 HttpContext 的一部分。 HttpRequest 类中没有 Range 属性,因此您必须查看 Headers。如果有Range,格式如下:

    Range: bytes=<start>-<end>

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

    Range: bytes=32768-98304

    您必须将文本解析为数字并进行相应处理。

    【讨论】:

    • 好答案。为了后代,一些示例代码会很好。一旦我实现它,我希望你不介意我将它注入你的答案。
    • @Larsenal:我没有提供示例代码,因为我没有。我记得几年前使用过这个,但代码早已不复存在。希望我的简短描述足以让您入门。我不介意你在我的答案中注入代码。
    • FWIW,这导致我找到以下帖子,其中包含显示如何处理远程请求的代码:blogs.visigo.com/chriscoulson/…
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      根据@brent-keller 的评论中链接到上面的博客文章——这又引用了CodePlex entry——我想出了下面的编辑。它使用 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-04-10
        • 1970-01-01
        • 2017-12-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-25
        相关资源
        最近更新 更多