当长度已知时,从Lazy ByteString构造RequestBodyStream

时间:2016-06-03 23:23:12

标签: haskell bytestring http-conduit

我正在尝试调整此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
如果我理解正确的话,上面代码中的

streamer类型为GivesPopper ()。 如果Lazy ByteString已知长度为len,那么在GivesPopper上编写{ "header": { "messageId": "42e0bf9c-18e2-424f-bb11-f8a12df1a79e", "name": "DiscoverAppliancesResponse", "namespace": "Alexa.ConnectedHome.Discovery", "payloadVersion": "2" }, "payload": { "discoveredAppliances": [ { "actions": [ "incrementPercentage", "decrementPercentage", "setPercentage", "turnOn", "turnOff" ], "applianceId": "0d6884ab-030e-8ff4-ffffaa15c06e0453", "friendlyDescription": "Study Light connected to Loxone Kit", "friendlyName": "Study Light", "isReachable": true, "manufacturerName": "Loxone", "modelName": "Spot" } ] } } 函数的好方法是什么?我们一次可以阅读一个块。

1 个答案:

答案 0 :(得分: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是获取下一个块的动作。第一次调用它时,您将获得原始的惰性字节串(严格的)。后续调用将始终返回空字符串。

<强>更新

以下是如何一次性捐出少量资金:

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