如何通过流式ByteString跟踪进度?

时间:2018-03-06 01:35:36

标签: haskell pipe pipeline bytestring haskell-streaming

我正在使用streaming-utils streaming-utils来传输HTTP响应正文。我希望跟踪与bytestring-progress允许使用惰性ByteString的方式类似的进度。我怀疑像toChunks这样的东西是必要的,然后减少一些累积的字节读取并返回未经修改的原始流。但是我无法弄明白,streaming文档非常缺乏帮助,大多数都是对替代库的宏大比较。

到目前为止,这是我付出的一些代码。它还不包括计数,只是尝试在流过(并且不编译)时打印块的大小。

download :: ByteString -> FilePath -> IO ()
download i file = do
  req <- parseRequest . C.unpack $ i
  m <- newHttpClientManager
  runResourceT $ do
    resp <- http req m
    lift . traceIO $ "downloading " <> file
    let body = SBS.fromChunks $ mapsM step $ SBS.toChunks $ responseBody resp
    SBS.writeFile file body

step bs = do
  traceIO $ "got " <> show (C.length bs) <> " bytes"
  return bs

1 个答案:

答案 0 :(得分:3)

我们想要的是以两种方式遍历Stream (Of ByteString) IO ()

  • 累积ByteString的传入长度并将更新打印到控制台。
  • 将流写入文件的人。

我们可以在copy函数的帮助下完成这项工作,该函数的类型为:

copy :: Monad m => Stream (Of a) m r -> Stream (Of a) (Stream (Of a) m) r

copy获取一个流并将其复制到两个不同的monadic层中,其中原始流的每个元素都由新分离的流的两个层发出。

(请注意,我们正在更改基本monad,而非更改为仿函数。将仿函数更改为另一个Stream的做法是将delimit groups改为单个流,我们对此不感兴趣在这里。)

以下函数获取流,复制它,使用S.scanprints them累积传入字符串的长度,并返回另一个仍可使用的流,例如将其写入文件:

{-# LANGUAGE OverloadedStrings #-}
import Streaming
import qualified Streaming.Prelude as S
import qualified Data.ByteString as B

track :: Stream (Of B.ByteString) IO r -> Stream (Of B.ByteString) IO r
track stream =
      S.mapM_ (liftIO . print) -- brings us back to the base monad, here another stream
    . S.scan (\s b -> s + B.length b) (0::Int) id
    $ S.copy stream

这将打印ByteString s以及累计长度:

main :: IO ()
main = S.mapM_ B.putStr . track $ S.each ["aa","bb","c"]