Как заставить 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
, но я не эксперт.