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

但请注意,这是不安全的,因为您对可以拥有的打开句柄的数量有限制,并且您只使用了其中一个,并且无法确定何时释放。

要解决这些问题,您应该只使用 Conduits(或构建在它之上的库)。在 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的文档说到达一个 EOF,句柄已关闭。您可以做的是在 run 命令之外使用hFileSizeand hGetContents,并且由于延迟评估,只有文件中所需的数据将在内存中。对于范围请求,您需要从hGetContents使用takedrop函数获得的字节串中获取数据。

于 2013-06-18T06:45:44.067 回答