1

これをHaskellに変換しようとしています。最終的には Conduits を使いたいと思っていますが、今のところは遅延 ByteString で十分でしょう。

だから私はこれを書いた

{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
module Main where

--import Data.Conduit.Binary
import Network.Wai
import Network.Wai.Handler.Warp
import Network.HTTP.Types
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Char8 as B
import Control.Monad.Trans
import System.IO
import Control.Exception
import Data.List.Split (splitOn)

video = "videos/big_buck_bunny.mp4"

main = bracket (openFile video ReadMode) (hClose) $ \h ->
    run 3000 $ \Request{..} -> do
        liftIO $ putStrLn "Connection"
        total <- liftIO $ hFileSize h
        case lookup "Range" requestHeaders of
            Nothing -> do
                v <- liftIO $ L.hGetContents h
                return $ responseLBS ok200 [("Content-Type", "video/mp4"), ("Connection","keep-alive"), ("Content-Length", B.pack $ show total)] v
            Just r -> do
                let (starts:ends:_) = splitOn "-" $ drop 6 $ B.unpack r
                    start = read starts
                    end = if not (null ends) then read ends else total - 1
                liftIO $ putStrLn $ "Range request " ++ show start ++ " " ++ show end
                liftIO $ hSeek h AbsoluteSeek start
                v <- liftIO $ L.hGetContents h
                return $ responseLBS partialContent206 [("Content-Type", "video/mp4")
                                                       , ("Accept-Ranges", "bytes")
                                                       , ("Content-Length", B.pack . show $ (end - start) + 1)
                                                       , ("Content-Range", B.pack $ concat ["bytes ", show start, "-", show end, "/", show total])
                                                       ] v

ただし、起動して開くとlocalhost:3000、次のエラーが表示されます。

Connection
send: resource vanished (Connection reset by peer)
Connection
Range request 0 159240553
send: resource vanished (Connection reset by peer)
Connection
Range request 158852274 159240553
Connection
videos/big_buck_bunny.mp4: hFileSize: illegal operation (handle is closed)
Connection
videos/big_buck_bunny.mp4: hFileSize: illegal operation (handle is closed)

これらのエラーが発生する理由、特にresource vanished. また、私が何もせずにハンドルが閉じられている理由もhClose

誰かが私がこれで間違っているところを教えてもらえますか?

4

2 に答える 2

1

最初のエラーについてはわかりませんが、コードに明らかなバグがあります:

            v <- liftIO $ L.hGetContents h

完了したらファイルを閉じることで知られている hGetContents を使用します。参照: http://hackage.haskell.org/packages/archive/bytestring/0.10.2.0/doc/html/Data-ByteString-Lazy.html#v:hGetContents

最初にファイルを提供した後、ハンドルが閉じられ、その後読み取り試行でエラーが発生します。

レイジー IO を使用したい場合は、安全ではなく健全でないという事実にもかかわらず、次のようなことを試すことができます。

            v <- liftIO $ L.readFile video

ただし、開いているハンドルの数には制限があり、そのうちの 1 つを使用したばかりで、いつ解放されるかがわからないため、安全ではないことに注意してください。

これらの問題に対処するには、コンジット (またはその上に構築されたライブラリ) を使用する必要があります。WAI には ResponseFile がありますが、これをどのように正確に行うべきかはわかりません: http://hackage.haskell.org/packages/archive/wai/1.4.0.1/doc/html/Network-Wai.html#v:応答ファイル

アップデート:

これは、ResponseFile を使用した作業コードです。https://gist.github.com/Tener/5803280

于 2013-06-18T06:39:30.617 に答える
0

hGetContentsのドキュメントには、1 つの EOF に達してハンドルが閉じられたと書かれています。あなたができることは、実行コマンドの外でhFileSizeandを取ることですhGetContents。遅延評価により、ファイルから必要なデータのみがメモリに残ります。範囲リクエストの場合、 and関数hGetContentsを使用して取得したバイト文字列からデータをフェッチする必要があります。takedrop

于 2013-06-18T06:45:44.067 に答える