如何用conduit将amazonka下载的S3流以恒定内存写入本地文件
问题2 (优先推荐)无需sinkLazy的恒定内存方案
你完全不需要先把响应体聚合为lazy ByteString再写入文件,sinkBody本身支持直接传入文件写入sink,实现边下载边写磁盘,全程内存占用仅和流的单个chunk大小挂钩,不会随文件体积增长:
- 先改造
getFile函数支持传入自定义sink:
getFileWithSink bucketName fileName sink = do resp <- send (getObject (BucketName bucketName) fileName) sinkBody (resp ^. gorsBody) sink
- 直接将
sinkFile作为参数传入,一步完成下载到本地的流程:
liftIO $ AWS.runResourceT $ runAwsT awsEnv $ getFileWithSink serviceBucket key (sinkFile filePath)
这个方案没有多余的内存拷贝,也不会有全量数据驻留内存,是性能最优的实现方式。
问题1 基于lazy ByteString的恒定内存写入方案
如果你确实需要先拿到完整的lazy ByteString做预处理(比如哈希校验、格式转换等),不要使用yield全量产出再转严格的写法,这种写法会强制把所有chunk拼接为单个严格ByteString,导致全量内存占用。
你可以直接使用Data.Conduit.Binary模块提供的sourceLazy,它会按lazy ByteString原生的chunk边界分批产出数据,不会把整个内容加载到内存:
import qualified Data.Conduit.Binary as CB -- 已获取lazy ByteString bytestream的前提下 liftIO $ runConduitRes $ CB.sourceLazy bytestream .| sinkFile filePath
只要你没有在代码的其他地方强制持有整个lazy ByteString的所有chunk,这个方案的内存占用会稳定在单chunk大小,不会随文件大小增长。
你之前直接用Data.ByteString.Lazy.writeFile出现内存翻倍的问题,本质是lazy求值的GC节奏不可控,旧chunk还没被回收就已经读入了新chunk,用conduit的流处理可以主动控制chunk的生命周期,规避这个问题。
内容的提问来源于stack exchange,提问作者Kiko Fernandez
相关产品推荐
相关产品推荐

