consumeZip

This commit is contained in:
Gregor Kleen 2017-10-05 14:10:32 +02:00
parent 104b3ad397
commit 0f0f77344f

View File

@ -1,6 +1,8 @@
{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE DeriveGeneric, DeriveDataTypeable #-}
{-# OPTIONS_GHC -fno-warn-missing-fields #-} -- This concerns Zip.zipEntrySize in produceZip {-# OPTIONS_GHC -fno-warn-missing-fields #-} -- This concerns Zip.zipEntrySize in produceZip
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Handler.Zip module Handler.Zip
( Zip.ZipError(..) ( Zip.ZipError(..)
@ -11,6 +13,8 @@ module Handler.Zip
import Import import Import
import qualified Data.Conduit.List as Conduit (map)
import qualified Codec.Archive.Zip.Conduit.Types as Zip import qualified Codec.Archive.Zip.Conduit.Types as Zip
import qualified Codec.Archive.Zip.Conduit.UnZip as Zip import qualified Codec.Archive.Zip.Conduit.UnZip as Zip
import qualified Codec.Archive.Zip.Conduit.Zip as Zip import qualified Codec.Archive.Zip.Conduit.Zip as Zip
@ -26,17 +30,49 @@ import qualified Data.Text.Encoding as Text
import System.FilePath import System.FilePath
import Data.Time import Data.Time
import GHC.Generics (Generic)
import Data.Typeable (Typeable)
data ZipEntry = ZipEntry data ZipEntry = ZipEntry
{ zipEntryName :: FilePath { zipEntryName :: FilePath
, zipEntryTime :: UTCTime , zipEntryTime :: UTCTime
, zipEntryContents :: Maybe Lazy.ByteString -- ^ 'Nothing' means this is a directory , zipEntryContents :: Maybe Lazy.ByteString -- ^ 'Nothing' means this is a directory
} } deriving (Read, Show, Generic, Typeable)
instance Default Zip.ZipInfo where
def = Zip.ZipInfo
{ zipComment = mempty
}
consumeZip :: (ZipEntry -> YesodDB UniWorX a) -- ^ Insert entries into database consumeZip :: (ZipEntry -> YesodDB UniWorX a) -- ^ Insert entries into database
-> Sink ByteString (YesodDB UniWorX) (Zip.ZipInfo, [a]) -> Sink ByteString (YesodDB UniWorX) (Zip.ZipInfo, [a])
consumeZip = error "consumeZip not implemented yet" consumeZip insertEntry = Zip.unZipStream `fuseBoth` consumeZip'
where
-- consumeZip' :: Sink (Either Zip.ZipEntry ByteString) (YesodDB UniWorX) [a]
consumeZip' = do
input <- await
case input of
Nothing -> return []
Just (Right _) -> throw $ userError "Data chunk in unexpected place when parsing ZIP"
Just (Left e) -> do
zipEntryName' <- either throw return . Text.decodeUtf8' $ Zip.zipEntryName e
contentChunks <- accContents
let
zipEntryName = normalise . makeValid $ Text.unpack zipEntryName'
zipEntryTime = localTimeToUTC utc $ Zip.zipEntryTime e
zipEntryContents
| hasTrailingPathSeparator zipEntryName = Nothing
| otherwise = Just $ Lazy.ByteString.fromChunks contentChunks
(:) <$> (lift $ insertEntry ZipEntry{..}) <*> consumeZip'
accContents :: Monad m => Sink (Either a b) m [b]
accContents = do
input <- await
case input of
Just (Right x) -> (x :) <$> accContents
_ -> return []
produceZip :: ( MonadBase b m produceZip :: ( MonadBase b m
@ -44,23 +80,20 @@ produceZip :: ( MonadBase b m
, MonadThrow m , MonadThrow m
) => Zip.ZipInfo ) => Zip.ZipInfo
-> Conduit ZipEntry m ByteString -> Conduit ZipEntry m ByteString
produceZip info = toZipData =$= void (Zip.zipStream zipOptions) produceZip info = Conduit.map toZipData =$= void (Zip.zipStream zipOptions)
where where
zipOptions = Zip.ZipOptions zipOptions = Zip.ZipOptions
{ zipOpt64 = True { zipOpt64 = True
, zipOptCompressLevel = -1 -- This is passed through all the way to the C zlib, where it means "default level" , zipOptCompressLevel = -1 -- This is passed through all the way to the C zlib, where it means "default level"
, zipOptInfo = info , zipOptInfo = info
} }
toZipData :: Monad m => Conduit ZipEntry m (Zip.ZipEntry, Zip.ZipData m)
toZipData = do toZipData :: Monad m => ZipEntry -> (Zip.ZipEntry, Zip.ZipData m)
entry <- await toZipData (e@ZipEntry{ zipEntryContents = Nothing })
case entry of = ((toZipEntry True e){ Zip.zipEntrySize = Nothing }, mempty)
Nothing toZipData (e@ZipEntry{ zipEntryContents = Just b})
-> return () = ((toZipEntry False e){ Zip.zipEntrySize = Just . fromIntegral $ Lazy.ByteString.length b }, Zip.ZipDataByteString b)
Just (e@ZipEntry{ zipEntryContents = Nothing })
-> yield ((toZipEntry True e){ Zip.zipEntrySize = Nothing }, mempty)
Just (e@ZipEntry{ zipEntryContents = Just b})
-> yield ((toZipEntry False e){ Zip.zipEntrySize = Just . fromIntegral $ Lazy.ByteString.length b }, Zip.ZipDataByteString b)
toZipEntry :: Bool -- ^ Is directory? toZipEntry :: Bool -- ^ Is directory?
-> ZipEntry -> Zip.ZipEntry -> ZipEntry -> Zip.ZipEntry
toZipEntry isDir ZipEntry{..} = Zip.ZipEntry toZipEntry isDir ZipEntry{..} = Zip.ZipEntry