|
39 | 39 | import Amazonka hiding (Error)
|
40 | 40 | import Amazonka.S3
|
41 | 41 | import Amazonka.S3.Lens
|
| 42 | +import qualified Amazonka.S3.StreamingUpload as SU |
42 | 43 | import CargoHold.API.Error
|
43 | 44 | import CargoHold.AWS (amazonkaEnvWithDownloadEndpoint)
|
44 | 45 | import qualified CargoHold.AWS as AWS
|
@@ -92,43 +93,52 @@ data S3AssetMeta = S3AssetMeta
|
92 | 93 | -- ignore the uploaded mimetype header and force it to be
|
93 | 94 | -- application/octet-stream.
|
94 | 95 |
|
95 |
| -uploadV3 :: |
96 |
| - V3.Principal -> |
97 |
| - V3.AssetKey -> |
98 |
| - V3.AssetHeaders -> |
99 |
| - Maybe V3.AssetToken -> |
100 |
| - ConduitM () ByteString (ResourceT IO) () -> |
101 |
| - ExceptT Error App () |
102 |
| -uploadV3 prc (s3Key . mkKey -> key) originalHeaders@(V3.AssetHeaders _ cl) tok src = do |
| 96 | +uploadV3 |
| 97 | + :: V3.Principal |
| 98 | + -> V3.AssetKey |
| 99 | + -> V3.AssetHeaders |
| 100 | + -> Maybe V3.AssetToken |
| 101 | + -> ConduitM () ByteString (ResourceT IO) () -- ^ streaming payload |
| 102 | + -> ExceptT Error App () |
| 103 | +uploadV3 prc (s3Key . mkKey -> key) (V3.AssetHeaders _ cl) tok src = do |
| 104 | + -- logging |
103 | 105 | Log.info $
|
104 |
| - "remote" .= val "S3" |
105 |
| - ~~ "asset.owner" .= toByteString prc |
106 |
| - ~~ "asset.key" .= key |
107 |
| - ~~ "asset.type_from_request_ignored" .= MIME.showType (V3.hdrType originalHeaders) |
108 |
| - ~~ "asset.type" .= MIME.showType ct |
109 |
| - ~~ "asset.size" .= cl |
110 |
| - ~~ msg (val "Uploading asset") |
111 |
| - void $ exec req |
| 106 | + "remote" .= val "S3" |
| 107 | + ~~ "asset.owner" .= toByteString prc |
| 108 | + ~~ "asset.key" .= key |
| 109 | + ~~ "asset.type" .= MIME.showType ct |
| 110 | + ~~ "asset.size" .= cl |
| 111 | + ~~ msg (val "Uploading asset") |
| 112 | + |
| 113 | + -- AWS environment (has bucket name & amazonka Env) |
| 114 | + awsEnv <- asks (.aws) |
| 115 | + |
| 116 | + -- build the “initiate multipart” request |
| 117 | + let createReq = |
| 118 | + newCreateMultipartUpload (BucketName awsEnv.s3Bucket) (ObjectKey key) |
| 119 | + & createMultipartUpload_contentType ?~ MIME.showType ct |
| 120 | + & createMultipartUpload_metadata .~ metaHeaders tok prc |
| 121 | + |
| 122 | + -- stream the conduit → multipart upload |
| 123 | + result <- liftIO . runResourceT . runConduit $ |
| 124 | + src |
| 125 | + .| chunksOfCE (fromIntegral defaultChunkSize) |
| 126 | + .| isolate (fromIntegral cl) |
| 127 | + .| SU.streamUpload awsEnv.amazonkaEnv Nothing createReq |
| 128 | + -- ^ Env ^ use library default part size |
| 129 | + -- ^ our create‑multipart request |
| 130 | + |
| 131 | + -- success / failure |
| 132 | + case result of |
| 133 | + Right _ -> pure () -- CompleteMultipartUploadResponse → done |
| 134 | + Left (_ :: (AbortMultipartUploadResponse, SomeException)) -> do |
| 135 | + Log.err $ |
| 136 | + "remote" .= val "S3" |
| 137 | + ~~ "asset.key" .= key |
| 138 | + ~~ msg (val "Multipart upload failed – aborted") |
| 139 | + throwE serverError |
112 | 140 | where
|
113 |
| - ct :: MIME.Type |
114 |
| - ct = octets -- See note on overrideMimeTypeAsOctetStream |
115 |
| - stream :: ConduitM () ByteString (ResourceT IO) () |
116 |
| - stream = |
117 |
| - src |
118 |
| - -- Rechunk bytestream to ensure we satisfy AWS's minimum chunk size |
119 |
| - -- on uploads |
120 |
| - .| chunksOfCE (fromIntegral defaultChunkSize) |
121 |
| - -- Ignore any 'junk' after the content; take only 'cl' bytes. |
122 |
| - .| isolate (fromIntegral cl) |
123 |
| - |
124 |
| - reqBdy :: ChunkedBody |
125 |
| - reqBdy = ChunkedBody defaultChunkSize (fromIntegral cl) stream |
126 |
| - |
127 |
| - req :: Text -> PutObject |
128 |
| - req b = |
129 |
| - newPutObject (BucketName b) (ObjectKey key) (toBody reqBdy) |
130 |
| - & putObject_contentType ?~ MIME.showType ct |
131 |
| - & putObject_metadata .~ metaHeaders tok prc |
| 141 | + ct = octets -- see note “overrideMimeTypeAsOctetStream” |
132 | 142 |
|
133 | 143 | -- | Turn a 'ResourceT IO' action into a pure @Conduit@.
|
134 | 144 | --
|
|
0 commit comments