如何使用conduit获取一个lazy ByteString并将其写入文件(在恒定内存中)



我正在使用amazonka流式传输S3文件的下载,并使用sinkBody函数继续流式传输。目前,我下载的文件如下:

getFile bucketName fileName = do
resp <- send (getObject (BucketName bucketName) fileName)
sinkBody (resp ^. gorsBody) sinkLazy

其中sinkBody :: MonadIO m => RsBody -> ConduitM ByteString Void (ResourceT IO) a -> m a。为了在恒定内存中运行,我认为sinkLazy是一个很好的选择,可以从导管流中获得一个值。

在此之后,我想将数据的惰性字节串(S3文件)保存到本地文件中,为此我使用以下代码:
-- fetch stream of data from S3
bytestream <- liftIO $ AWS.runResourceT $ runAwsT awsEnv $ getFile serviceBucket key
-- create a file
liftIO $ writeFile filePath  ""
-- write content of stream into the file (strict version), keeps data in memory...
liftIO $ runConduitRes $ yield bytestream .| mapC B.toStrict .| sinkFile filePath

但是这段代码有一个缺陷,我需要"意识到"。所有的惰性字节串都在内存中,这意味着它不能在固定空间中运行。

  • 是否有任何方法,我可以使用管道yield一个懒惰的字节串,并将其保存到一个文件在恒定的内存?

  • 或者,任何其他不使用sinkLazy并解决保存到在恒定空间中运行的文件的问题的方法?

编辑

我还测试了将惰性字节流直接写入文件,如下所示,但这会消耗大约2倍于文件大小的内存。(writeFile来自Data.ByteString.Lazy)。

bytestream <- liftIO $ AWS.runResourceT $ runAwsT awsEnv $ getFile serviceBucket key
writeFile filename bytestream

conduit这样的流库的目的是实现惰性数据结构和操作(惰性ByteStrings,惰性I/O等)的一些好处,同时更好地控制内存使用。sinkLazy函数的目的是将数据从具有良好控制的内存占用的conduit生态系统中取出,并回到带有相关空间泄漏的惰性对象的狂野西部。所以,这就是你的问题。

与其将数据流从conduit接收到延迟的ByteString中,不如将数据保存在conduit中,并使用类似sinkFile的方式将数据流直接接收到文件中。我没有启动和运行的AWS测试程序,但是下面的类型检查可能会满足您的要求:

import Conduit
import Control.Lens
import Network.AWS
import Network.AWS.S3
getFile bucketName fileName outputFileName = do
resp <- send (getObject (BucketName bucketName) fileName)
sinkBody (resp ^. gorsBody) (sinkFile outputFileName)

最新更新