我一直在尝试将this转换为Haskell。最终我想使用Conduits,但是现在,我希望懒惰的ByteString会这样做。
所以我wrote this:
{-# 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
的情况下关闭句柄。
有人会暗示我出错的地方吗?
答案 0 :(得分: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
<强>更新强>
这是使用ResponseFile的工作代码。 https://gist.github.com/Tener/5803280
答案 1 :(得分:0)
hGetContents的documentation表示已达到一个EOF,句柄已关闭。
你可以做的是在运行命令之外取hFileSize
和hGetContents
,由于延迟评估,只有文件中所需的数据才会在内存中。对于范围请求,您需要使用hGetContents
和take
函数从drop
获取字节字符串中的数据。