Как заставить chunking работать с амазонкой, кабелепроводом и ленивым байтом

Я написал код ниже для имитации загрузки на S3 от Lazy ByteString (который будет получен через сетевой сокет. Здесь мы моделируем чтение файла размером ~100 МБ). Проблема с приведенным ниже кодом заключается в том, что он заставляет читать весь файл в память, а не разбивать его на части (cbytes) - порадует вас указателями на то, почему чанкинг не работает:

import Control.Lens
import Network.AWS
import Network.AWS.S3
import Network.AWS.Data.Body
import System.IO
import           Data.Conduit (($$+-))
import           Data.Conduit.Binary (sinkLbs,sourceLbs)
import qualified Data.Conduit.List as CL (mapM_)
import           Network.HTTP.Conduit (responseBody,RequestBody(..),newManager,tlsManagerSettings)
import qualified Data.ByteString.Lazy as LBS

example :: IO PutObjectResponse
example = do
    -- To specify configuration preferences, newEnv is used to create a new Env. The Region denotes the AWS region requests will be performed against,
    -- and Credentials is used to specify the desired mechanism for supplying or retrieving AuthN/AuthZ information.
    -- In this case, Discover will cause the library to try a number of options such as default environment variables, or an instance's IAM Profile:
    e <- newEnv NorthVirginia Discover

    -- A new Logger to replace the default noop logger is created, with the logger set to print debug information and errors to stdout:
    l <- newLogger Debug stdout

    -- The payload for the S3 object is retrieved from a file that simulates lazy bytestring received over network
    inb <- LBS.readFile "out"
    lenb <- System.IO.withFile "out" ReadMode hFileSize -- evaluates to 104857600 (100MB)
    let cbytes = toBody $ ChunkedBody (1024*128) (fromIntegral lenb) (sourceLbs inb)

    -- We now run the AWS computation with the overriden logger, performing the PutObject request:
    runResourceT . runAWS (e & envLogger .~ l) $
        send ((putObject "yourtestenv-change-it-please" "testbucket/test" cbytes) & poContentType .~ Just "text; charset=UTF-8")

main = example >> return ()

Запуск исполняемого файла с RTS -s опция показывает, что вся вещь читается в память (максимальная резидентность ~113 МБ - я видел ~87 МБ однажды). С другой стороны, если я использую chunkedFile, он корректно разделен на части (максимальная резидентность ~ 10 МБ).

2 ответа

Решение

Это понятно

  inb <- LBS.readFile "out"
  lenb <- System.IO.withFile "out" ReadMode hFileSize -- evaluates to 104857600 (100MB)
  let cbytes = toBody $ ChunkedBody (1024*128) (fromIntegral lenb) (sourceLbs inb)

следует переписать как

  lenb <- System.IO.withFile "out" ReadMode hFileSize -- evaluates to 104857600 (100MB)
  let cbytes = toBody $ ChunkedBody (1024*128) (fromIntegral lenb) (C.sourceFile "out")

Как вы написали, цель проводников побеждена. Весь файл должен быть накоплен LBS.readFile, но затем разбил кусок на кусок, когда его кормили sourceLBS, (Если ленивый IO работает правильно, это может не произойти.) sourceFile читает файл постепенно, порция за порцией Может быть так, например, toBody накапливает весь файл, и в этом случае точка каналов нарушается в другой точке. Взглянув на источник для send и так далее, я не вижу ничего, что могло бы сделать это, хотя.

Я не уверен, но я думаю, что виновник LBS.readFile его документация гласит:

readFile :: FilePath -> IO ByteString

Read an entire file lazily into a ByteString.
The Handle will be held open until EOF is encountered.

chunkedFile работает в качестве канала - в качестве альтернативы вы могли бы использовать

sourceFile :: MonadResource m => FilePath -> Producer m ByteString

от (conduit-extras/Data.Conduit.Binary) вместо LBS.readFile, но я не эксперт.

Другие вопросы по тегам