【问题标题】:Constructing RequestBodyStream from Lazy ByteString when length is known当长度已知时从 Lazy ByteString 构造 RequestBodyStream
【发布时间】:2016-06-03 23:23:12
【问题描述】:

我正在尝试调整这个AWS S3 upload code 来处理长度已知的Lazy ByteString(这样它就不会被强制在内存中全部读取——它会通过预先发送长度的网络来传输)。看来我必须在Lazy ByteString 上定义一个GivesPopper 函数才能将其转换为RequestBodyStream。由于定义了GivesPopper 的复杂方式,我不确定如何为Lazy ByteString 编写它。将欣赏有关如何编写它的指针。 Here 是从文件中读取的写法:

let file ="test"
-- streams large file content, without buffering more than 10k in memory
let streamer sink = withFile file ReadMode $ \h -> sink $ S.hGet h 10240

如果我理解正确,上面代码中的streamerGivesPopper () 类型。 给定一个已知长度lenLazy ByteString,在它上面写GivesPopper 函数的好方法是什么?我们可以一次读取一个块。

【问题讨论】:

    标签: haskell bytestring http-conduit


    【解决方案1】:

    这就是你要找的吗?

    import qualified Data.ByteString as S
    import qualified Data.ByteString.Lazy as L
    import System.IO
    
    file = "test"
    -- original streamer for feeding a sink from a file
    streamer :: (IO S.ByteString -> IO r) -> IO r
    streamer sink = withFile file ReadMode $ \h -> sink $ S.hGet h 10240
    
    -- feed a lazy ByteString to sink    
    lstreamer :: L.ByteString -> (IO S.ByteString -> IO r) -> IO r
    lstreamer lbs sink = sink (return (L.toStrict lbs))
    

    lstreamer 类型检查,但可能不会完全按照您的要求执行。每次接收器调用它时,它都会返回相同的数据。另一方面,S.hGet h ... 最终会返回空字符串。

    这是一个使用 IORef 来跟踪我们是否应该开始返回空字符串的解决方案:

    import Data.IORef
    
    mklstream :: L.ByteString -> (IO S.ByteString -> IO r) -> IO r
    mklstream lbs sink = do
      ref <- newIORef False
      let fetch :: IO S.ByteString
          fetch = do sent <- readIORef ref
                     writeIORef ref True
                     if sent
                       then return S.empty
                       else return (L.toStrict lbs)
      sink fetch
    

    这里fetch 是获取下一个块的动作。第一次调用它时,您将获得原始的惰性 Bytestring(严格)。后续调用将始终返回空字符串。

    更新

    以下是一次发放少量的方法:

    mklstream :: L.ByteString -> (IO S.ByteString -> IO r) -> IO r
    mklstream lbs sink = do
      ref <- newIORef (L.toChunks lbs)
      let fetch :: IO S.ByteString
          fetch = do chunks <- readIORef ref
                     case chunks of
                       [] -> return S.empty
                       (c:cs) -> do writeIORef ref cs
                                    return c
      sink fetch
    

    【讨论】:

    • 这个解决方案不是因为L.toStrict调用而将整个惰性字节串读入内存吗?我们希望一次读取一个块,或者设置内存使用上限。
    • 好吧 - 它向您展示了如何创建一些状态(IORef)来跟踪您已经给出的内容。请注意,除非您使用惰性 IO,否则它们的惰性字节字符串已经在内存中。
    • 应该说更新的mklstream依赖于惰性字节串永远不会有空字节串块的不变量。如果 ref 是用newIORef (filter (not . S.null) (L.toChunks lbs)) 定义的,它会更清楚地满足http-client 的要求,但如果我理解的话,它不需要这样做。然后它具有所需的结构,就像hGet
    • io-streams 或多或少地预先打包了相同的结果。在那里你会写ref &lt;- Streams.fromLazyByteString lbs 然后fetch 函数将类似于fmap (maybe S.empty id) (Streams.read ref)
    • @Michael,是的,关于io-streams 的好点。我看了一下那里的实现。想知道上面代码中的writeIORef 是否有效,因为它弹出堆栈顶部的方式是在删除顶部元素后重写新堆栈。 io-streamsmakeInputStream 中也有类似的逻辑。所以,我猜它可能是有效的。
    猜你喜欢
    • 2011-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-22
    • 2013-10-28
    相关资源
    最近更新 更多