WebDAV: Avoid buffering whole file in memory when downloading.
httpBodyRetriever will later also be used by S3 This commit was sponsored by Ethan Aubin.
This commit is contained in:
parent
fc17cf852e
commit
c3f8512475
4 changed files with 37 additions and 23 deletions
|
@ -11,7 +11,7 @@ import Common.Annex
|
||||||
import Types.StoreRetrieve
|
import Types.StoreRetrieve
|
||||||
import Utility.Metered
|
import Utility.Metered
|
||||||
import Remote.Helper.Special
|
import Remote.Helper.Special
|
||||||
import Network.HTTP.Client (RequestBody(..))
|
import Network.HTTP.Client (RequestBody(..), Response, responseBody, BodyReader)
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as L
|
import qualified Data.ByteString.Lazy as L
|
||||||
import qualified Data.ByteString as S
|
import qualified Data.ByteString as S
|
||||||
|
@ -35,5 +35,19 @@ httpStorer a = fileStorer $ \k f m -> do
|
||||||
pop [] = ([], S.empty)
|
pop [] = ([], S.empty)
|
||||||
pop (c:cs) = (cs, c)
|
pop (c:cs) = (cs, c)
|
||||||
|
|
||||||
--httpRetriever :: (Key -> Annex Response) -> Retriever
|
-- Reads the http body and stores it to the specified file, updating the
|
||||||
--httpRetriever a = byteRetriever $ \k sink
|
-- meter as it goes.
|
||||||
|
httpBodyRetriever :: FilePath -> MeterUpdate -> Response BodyReader -> IO ()
|
||||||
|
httpBodyRetriever dest meterupdate resp =
|
||||||
|
bracket (openBinaryFile dest WriteMode) hClose (go zeroBytesProcessed)
|
||||||
|
where
|
||||||
|
reader = responseBody resp
|
||||||
|
go sofar h = do
|
||||||
|
b <- reader
|
||||||
|
if S.null b
|
||||||
|
then return ()
|
||||||
|
else do
|
||||||
|
let sofar' = addBytesProcessed sofar $ S.length b
|
||||||
|
S.hPut h b
|
||||||
|
meterupdate sofar'
|
||||||
|
go sofar' h
|
||||||
|
|
|
@ -97,7 +97,7 @@ store (LegacyChunks chunksize) (Just dav) = fileStorer $ \k f p -> liftIO $
|
||||||
withMeteredFile f p $ storeLegacyChunked chunksize k dav
|
withMeteredFile f p $ storeLegacyChunked chunksize k dav
|
||||||
store _ (Just dav) = httpStorer $ \k reqbody -> liftIO $ goDAV dav $ do
|
store _ (Just dav) = httpStorer $ \k reqbody -> liftIO $ goDAV dav $ do
|
||||||
let tmp = keyTmpLocation k
|
let tmp = keyTmpLocation k
|
||||||
let dest = keyLocation k ++ keyFile k
|
let dest = keyLocation k
|
||||||
void $ mkColRecursive tmpDir
|
void $ mkColRecursive tmpDir
|
||||||
inLocation tmp $
|
inLocation tmp $
|
||||||
putContentM' (contentType, reqbody)
|
putContentM' (contentType, reqbody)
|
||||||
|
@ -117,16 +117,10 @@ retrieve :: ChunkConfig -> Maybe DavHandle -> Retriever
|
||||||
retrieve _ Nothing = error "unable to connect"
|
retrieve _ Nothing = error "unable to connect"
|
||||||
retrieve (LegacyChunks _) (Just dav) = retrieveLegacyChunked dav
|
retrieve (LegacyChunks _) (Just dav) = retrieveLegacyChunked dav
|
||||||
retrieve _ (Just dav) = fileRetriever $ \d k p -> liftIO $
|
retrieve _ (Just dav) = fileRetriever $ \d k p -> liftIO $
|
||||||
meteredWriteFile p d =<< getDAV dav (keyLocation k ++ keyFile k)
|
goDAV dav $
|
||||||
|
inLocation (keyLocation k) $
|
||||||
getDAV :: DavHandle -> DavLocation -> IO L.ByteString
|
withContentM $
|
||||||
getDAV dav l = do
|
httpBodyRetriever d p
|
||||||
mb <- goDAV dav $ safely $
|
|
||||||
inLocation l $
|
|
||||||
snd <$> getContentM
|
|
||||||
case mb of
|
|
||||||
Nothing -> error "download failed"
|
|
||||||
Just b -> return b
|
|
||||||
|
|
||||||
remove :: Maybe DavHandle -> Remover
|
remove :: Maybe DavHandle -> Remover
|
||||||
remove Nothing _ = return False
|
remove Nothing _ = return False
|
||||||
|
@ -134,7 +128,7 @@ remove (Just dav) k = liftIO $ do
|
||||||
-- Delete the key's whole directory, including any
|
-- Delete the key's whole directory, including any
|
||||||
-- legacy chunked files, etc, in a single action.
|
-- legacy chunked files, etc, in a single action.
|
||||||
ret <- goDAV dav $ safely $
|
ret <- goDAV dav $ safely $
|
||||||
inLocation (keyLocation k) delContentM
|
inLocation (keyDir k) delContentM
|
||||||
return (isJust ret)
|
return (isJust ret)
|
||||||
|
|
||||||
checkKey :: Remote -> ChunkConfig -> Maybe DavHandle -> CheckPresent
|
checkKey :: Remote -> ChunkConfig -> Maybe DavHandle -> CheckPresent
|
||||||
|
@ -145,7 +139,7 @@ checkKey r chunkconfig (Just dav) k = do
|
||||||
LegacyChunks _ -> checkKeyLegacyChunked dav k
|
LegacyChunks _ -> checkKeyLegacyChunked dav k
|
||||||
_ -> do
|
_ -> do
|
||||||
v <- liftIO $ goDAV dav $
|
v <- liftIO $ goDAV dav $
|
||||||
existsDAV (keyLocation k ++ keyFile k)
|
existsDAV (keyLocation k)
|
||||||
either error return v
|
either error return v
|
||||||
|
|
||||||
configUrl :: Remote -> Maybe URLString
|
configUrl :: Remote -> Maybe URLString
|
||||||
|
@ -315,13 +309,15 @@ storeLegacyChunked chunksize k dav b =
|
||||||
finalizeStore (baseURL dav) tmp' (fromJust $ locationParent dest')
|
finalizeStore (baseURL dav) tmp' (fromJust $ locationParent dest')
|
||||||
|
|
||||||
tmp = keyTmpLocation k
|
tmp = keyTmpLocation k
|
||||||
dest = keyLocation k ++ keyFile k
|
dest = keyLocation k
|
||||||
|
|
||||||
retrieveLegacyChunked :: DavHandle -> Retriever
|
retrieveLegacyChunked :: DavHandle -> Retriever
|
||||||
retrieveLegacyChunked dav = fileRetriever $ \d k p -> liftIO $
|
retrieveLegacyChunked dav = fileRetriever $ \d k p -> liftIO $
|
||||||
withStoredFilesLegacyChunked k dav onerr $ \locs ->
|
withStoredFilesLegacyChunked k dav onerr $ \locs ->
|
||||||
Legacy.meteredWriteFileChunks p d locs $
|
Legacy.meteredWriteFileChunks p d locs $ \l ->
|
||||||
getDAV dav
|
goDAV dav $
|
||||||
|
inLocation l $
|
||||||
|
snd <$> getContentM
|
||||||
where
|
where
|
||||||
onerr = error "download failed"
|
onerr = error "download failed"
|
||||||
|
|
||||||
|
@ -365,4 +361,4 @@ withStoredFilesLegacyChunked k dav onerr a = do
|
||||||
then onerr chunkcount
|
then onerr chunkcount
|
||||||
else a chunks
|
else a chunks
|
||||||
where
|
where
|
||||||
keyloc = keyLocation k ++ keyFile k
|
keyloc = keyLocation k
|
||||||
|
|
|
@ -29,8 +29,8 @@ inLocation :: (MonadIO m) => DavLocation -> DAVT m a -> DAVT m a
|
||||||
inLocation d = inDAVLocation (</> d)
|
inLocation d = inDAVLocation (</> d)
|
||||||
|
|
||||||
{- The directory where files(s) for a key are stored. -}
|
{- The directory where files(s) for a key are stored. -}
|
||||||
keyLocation :: Key -> DavLocation
|
keyDir :: Key -> DavLocation
|
||||||
keyLocation k = addTrailingPathSeparator $ hashdir </> keyFile k
|
keyDir k = addTrailingPathSeparator $ hashdir </> keyFile k
|
||||||
where
|
where
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
hashdir = hashDirLower k
|
hashdir = hashDirLower k
|
||||||
|
@ -38,6 +38,9 @@ keyLocation k = addTrailingPathSeparator $ hashdir </> keyFile k
|
||||||
hashdir = replace "\\" "/" (hashDirLower k)
|
hashdir = replace "\\" "/" (hashDirLower k)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
keyLocation :: Key -> DavLocation
|
||||||
|
keyLocation k = keyDir k ++ keyFile k
|
||||||
|
|
||||||
{- Where we store temporary data for a key as it's being uploaded. -}
|
{- Where we store temporary data for a key as it's being uploaded. -}
|
||||||
keyTmpLocation :: Key -> DavLocation
|
keyTmpLocation :: Key -> DavLocation
|
||||||
keyTmpLocation = addTrailingPathSeparator . tmpLocation . keyFile
|
keyTmpLocation = addTrailingPathSeparator . tmpLocation . keyFile
|
||||||
|
|
3
debian/changelog
vendored
3
debian/changelog
vendored
|
@ -18,7 +18,8 @@ git-annex (5.20140718) UNRELEASED; urgency=medium
|
||||||
* Display exception message when a transfer fails due to an exception.
|
* Display exception message when a transfer fails due to an exception.
|
||||||
* WebDAV: Sped up by avoiding making multiple http connections
|
* WebDAV: Sped up by avoiding making multiple http connections
|
||||||
when storing a file.
|
when storing a file.
|
||||||
* WebDAV: Avoid buffering whole file in memory when uploading.
|
* WebDAV: Avoid buffering whole file in memory when uploading and
|
||||||
|
downloading.
|
||||||
* WebDAV: Dropped support for DAV before 1.0.
|
* WebDAV: Dropped support for DAV before 1.0.
|
||||||
* testremote: New command to test uploads/downloads to a remote.
|
* testremote: New command to test uploads/downloads to a remote.
|
||||||
* Dropping an object from a bup special remote now deletes the git branch
|
* Dropping an object from a bup special remote now deletes the git branch
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue