@@ -70,7 +70,7 @@ module Mismi.S3.Commands (
7070 ) where
7171
7272import Control.Arrow ((***) )
73-
73+ import Control.Concurrent.Async.Lifted ( mapConcurrently_ )
7474import Control.Exception (ioError )
7575import qualified Control.Exception as CE
7676import Control.Lens ((.~) , (^.) , to , view )
@@ -124,7 +124,6 @@ import System.Directory (createDirectoryIfMissing, doesFileExist, getD
124124import System.FilePath (FilePath , (</>) , takeDirectory )
125125import System.Posix.IO (OpenMode (.. ), openFd , closeFd , fdSeek , defaultFileFlags )
126126import System.Posix.Files (fileSize , getFileStatus , isDirectory , isRegularFile )
127- import System.Posix.Types (FileOffset )
128127import qualified "unix-bytestring" System.Posix.IO.ByteString as UBS
129128
130129import System.Timeout.Lifted (timeout )
@@ -372,13 +371,13 @@ upload :: FilePath -> Address -> EitherT UploadError AWS ()
372371upload =
373372 uploadWithMode Fail
374373
375- uploadRecursive :: FilePath -> Address -> EitherT UploadError AWS ()
374+ uploadRecursive :: FilePath -> Address -> Int -> EitherT UploadError AWS ()
376375uploadRecursive =
377376 uploadRecursiveWithMode Fail
378377
379- uploadRecursiveOrFail :: FilePath -> Address -> AWS ()
380- uploadRecursiveOrFail f a =
381- eitherT hoistUploadError pure $ uploadRecursive f a
378+ uploadRecursiveOrFail :: FilePath -> Address -> Int -> AWS ()
379+ uploadRecursiveOrFail f a i =
380+ eitherT hoistUploadError pure $ uploadRecursive f a i
382381
383382uploadOrFail :: FilePath -> Address -> AWS ()
384383uploadOrFail f a =
@@ -388,9 +387,9 @@ uploadWithModeOrFail :: WriteMode -> FilePath -> Address -> AWS ()
388387uploadWithModeOrFail w f a =
389388 eitherT hoistUploadError pure $ uploadWithMode w f a
390389
391- uploadRecursiveWithModeOrFail :: WriteMode -> FilePath -> Address -> AWS ()
392- uploadRecursiveWithModeOrFail w f a =
393- eitherT hoistUploadError pure $ uploadRecursiveWithMode w f a
390+ uploadRecursiveWithModeOrFail :: WriteMode -> FilePath -> Address -> Int -> AWS ()
391+ uploadRecursiveWithModeOrFail w f a i =
392+ eitherT hoistUploadError pure $ uploadRecursiveWithMode w f a i
394393
395394hoistUploadError :: UploadError -> AWS ()
396395hoistUploadError e =
@@ -439,11 +438,11 @@ uploadSingle file a = do
439438 void . send $ f' A. putObject a rq & A. poServerSideEncryption .~ pure sse
440439
441440multipartUpload :: FilePath -> Address -> Integer -> Integer -> Int -> EitherT UploadError AWS ()
442- multipartUpload file a fileSize chunk fork = do
441+ multipartUpload file a fSize chunk fork = do
443442 e <- ask
444443 mpu <- lift $ createMultipartUpload a
445444
446- let chunks = calculateChunksCapped (fromInteger fileSize ) (fromInteger chunk) 4096 -- max 4096 prts returned
445+ let chunks = calculateChunksCapped (fromInteger fSize ) (fromInteger chunk) 4096 -- max 4096 prts returned
447446
448447 r <- liftIO $
449448 consume (forM_ chunks . writeQueue) fork $ multipartUploadWorker e mpu file a
@@ -481,40 +480,46 @@ multipartUploadWorker e mpu file a (o, c, i) =
481480 pure $! Right $! PartResponse i m
482481
483482
484- uploadRecursiveWithMode :: WriteMode -> FilePath -> Address -> EitherT UploadError AWS ()
485- uploadRecursiveWithMode m src (Address buck ky) = do
483+ uploadRecursiveWithMode :: WriteMode -> FilePath -> Address -> Int -> EitherT UploadError AWS ()
484+ uploadRecursiveWithMode mode src (Address buck ky) fork = do
486485 es <- tryIO $ getFileStatus src
487486 case es of
488487 Left _ -> left $ UploadSourceMissing src
489488 Right st -> unless (isDirectory st) . left $ UploadSourceNotDirectory src
490- files <- fmap fst <$> liftIO (listRecursivelyLocal src)
491- let outputAddrs = fmap (\ fp -> Address buck (ky // Key (T. pack $ L. drop prefixLen fp))) files
492- mapM_ (uncurry (uploadWithMode m)) $ L. zip files outputAddrs
489+ files <- liftIO (listRecursivelyLocal src)
490+ mapM_ uploadFiles $ chunkFilesBySize fork (fromIntegral bigChunkSize) files
493491 where
492+ uploadFiles :: [(FilePath , Int64 )] -> EitherT UploadError AWS ()
493+ uploadFiles [] = pure ()
494+ uploadFiles [(f,s)]
495+ | fromIntegral s < bigChunkSize = lift . uploadSingle f $ uploadAddress f
496+ | otherwise = uploadWithMode mode f $ uploadAddress f
497+ uploadFiles xs =
498+ mapConcurrently_ (\ (f, _) -> lift . uploadSingle f $ uploadAddress f) xs
499+
500+
494501 prefixLen = L. length (src </> " a" ) - 1
495502
496- -- uploadAddress :: FilePath -> Address
497- -- uploadAddress fp = Address buck (ky // Key (T.pack $ L.drop prefixLen fp))
503+ uploadAddress :: FilePath -> Address
504+ uploadAddress fp = Address buck (ky // Key (T. pack $ L. drop prefixLen fp))
498505
499- -- Take a list of files and their sizes, and return a list of list of files
500- -- where the total size of of the files in the sub list is less than `bigChunkSize `
506+ -- Take a list of files and their sizes, and convert it to a list of tests
507+ -- where the total size of of the files in the sub list is less than `maxSize `
501508-- and the length of the sub lists is <= `maxCount`.
502- chunkFilesBySize :: Int -> Int64 -> [(FilePath , Int64 )] -> [[FilePath ]]
509+ chunkFilesBySize :: Int -> Int64 -> [(FilePath , Int64 )] -> [[( FilePath , Int64 ) ]]
503510chunkFilesBySize maxCount maxSize =
504511 takeFiles 0 [] . L. sortOn snd
505512 where
506- takeFiles :: Int64 -> [FilePath ] -> [(FilePath , Int64 )] -> [[FilePath ]]
513+ takeFiles :: Int64 -> [( FilePath , Int64 ) ] -> [(FilePath , Int64 )] -> [[( FilePath , Int64 ) ]]
507514 takeFiles _ acc [] = [acc]
508515 takeFiles current acc ((x, s): xs) =
509516 if current + s < maxSize && L. length acc < maxCount
510- then takeFiles (current + s) (x: acc) xs
511- else (x: acc) : takeFiles 0 [] xs
512-
513- -- bigChunkSize
517+ then takeFiles (current + s) ((x, s): acc) xs
518+ else ((x, s): acc) : takeFiles 0 [] xs
514519
515520-- | Like `listRecursively` but for the local filesystem.
516521-- Also returns
517- listRecursivelyLocal :: MonadIO m => FilePath -> m [(FilePath , FileOffset )]
522+ listRecursivelyLocal :: MonadIO m => FilePath -> m [(FilePath , Int64 )]
518523listRecursivelyLocal topdir = do
519524 entries <- liftIO $ listDirectory topdir
520525 (dirs, files) <- liftIO . partitionDirsFilesWithSizes $ fmap (topdir </> ) entries
@@ -530,16 +535,17 @@ listDirectory path =
530535 f filename =
531536 filename /= " ." && filename /= " .."
532537
533- partitionDirsFilesWithSizes :: MonadIO m => [FilePath ] -> m ([FilePath ], [(FilePath , FileOffset )])
538+ partitionDirsFilesWithSizes :: MonadIO m => [FilePath ] -> m ([FilePath ], [(FilePath , Int64 )])
534539partitionDirsFilesWithSizes =
535540 pworker ([] , [] )
536541 where
537542 pworker (dirs, files) [] = pure (dirs, files)
538543 pworker (dirs, files) (x: xs) = do
539544 xstat <- liftIO $ getFileStatus x
540- pworker
541- (if isDirectory xstat then x : dirs else dirs, if isRegularFile xstat then (x, fileSize xstat) : files else files)
542- xs
545+ let xsize = fromIntegral $ fileSize xstat
546+ newDirs = if isDirectory xstat then x : dirs else dirs
547+ newFiles = if isRegularFile xstat then (x, xsize) : files else files
548+ pworker (newDirs, newFiles) xs
543549
544550write :: Address -> Text -> AWS WriteResult
545551write =
0 commit comments