1

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

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 ()如果我理解正确,上面的代码是类型。给定一个Lazy ByteString已知长度的,在它上面写函数len的好方法是什么?GivesPopper我们可以一次读取一个块。

4

1 回答 1

2

这是你要找的吗?

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
于 2016-06-04T01:11:11.130 回答