aws-s3-multipart.mdx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. ---
  2. sidebar_position: 4
  3. slug: /aws-s3-multipart
  4. ---
  5. import Tabs from '@theme/Tabs';
  6. import TabItem from '@theme/TabItem';
  7. import UppyCdnExample from '/src/components/UppyCdnExample';
  8. # AWS S3
  9. The `@uppy/aws-s3` plugin can be used to upload files directly to a S3 bucket or
  10. a S3-compatible provider, such as Google Cloud Storage or DigitalOcean Spaces.
  11. Uploads can be signed using either [Companion][companion docs], temporary
  12. credentials, or a custom signing function.
  13. ## When should I use it?
  14. :::tip
  15. Not sure which uploader is best for you? Read
  16. “[Choosing the uploader you need](/docs/guides/choosing-uploader)”.
  17. :::
  18. You can use this plugin when you prefer a _client-to-storage_ over a
  19. _client-to-server-to-storage_ (such as [Transloadit](/docs/transloadit) or
  20. [Tus](/docs/tus)) setup. This may in some cases be preferable, for instance, to
  21. reduce costs or the complexity of running a server and load balancer with
  22. [Tus](/docs/tus).
  23. Multipart uploads start to become valuable for larger files (100 MiB+) as
  24. it uploads a single object as a set of parts. This has certain benefits, such as
  25. improved throughput (uploading parts in parallel) and quick recovery from
  26. network issues (only the failed parts need to be retried). The downside is
  27. request overhead, as it needs to do creation, signing (unless you are [signing
  28. on the client][]), and completion requests besides the upload requests. For
  29. example, if you are uploading files that are only a couple kilobytes with a
  30. 100ms roundtrip latency, you are spending 400ms on overhead and only a few
  31. milliseconds on uploading.
  32. **In short**
  33. - We recommend to set [`shouldUseMultipart`][] to enable multipart uploads only
  34. for large files.
  35. - If you prefer to have less overhead (+20% upload speed) you can use temporary
  36. S3 credentials with [`getTemporarySecurityCredentials`][]. This means users
  37. get a single token which allows them to do bucket operations for longer,
  38. instead of short lived signed URL per resource. This is a security trade-off.
  39. ## Install
  40. <Tabs>
  41. <TabItem value="npm" label="NPM" default>
  42. ```shell
  43. npm install @uppy/aws-s3
  44. ```
  45. </TabItem>
  46. <TabItem value="yarn" label="Yarn">
  47. ```shell
  48. yarn add @uppy/aws-s3
  49. ```
  50. </TabItem>
  51. <TabItem value="cdn" label="CDN">
  52. <UppyCdnExample>
  53. {`
  54. import { Uppy, AwsS3 } from "{{UPPY_JS_URL}}"
  55. new Uppy().use(AwsS3, { /* see options */ })
  56. `}
  57. </UppyCdnExample>
  58. </TabItem>
  59. </Tabs>
  60. ## Use
  61. ### Setting up your S3 bucket
  62. To use this plugin with S3 we need to setup a bucket with the right permissions
  63. and CORS settings.
  64. S3 buckets do not allow public uploads for security reasons. To allow Uppy and
  65. the browser to upload directly to a bucket, its CORS permissions need to be
  66. configured.
  67. CORS permissions can be found in the
  68. [S3 Management Console](https://console.aws.amazon.com/s3/home). Click the
  69. bucket that will receive the uploads, then go into the `Permissions` tab and
  70. select the `CORS configuration` button. A JSON document will be shown that
  71. defines the CORS configuration. (AWS used to use XML but now only allow JSON).
  72. More information about the
  73. [S3 CORS format here](https://docs.amazonaws.cn/en_us/AmazonS3/latest/userguide/ManageCorsUsing.html).
  74. The configuration required for Uppy and Companion is this:
  75. ```json
  76. [
  77. {
  78. "AllowedOrigins": ["https://my-app.com"],
  79. "AllowedMethods": ["GET", "PUT"],
  80. "MaxAgeSeconds": 3000,
  81. "AllowedHeaders": [
  82. "Authorization",
  83. "x-amz-date",
  84. "x-amz-content-sha256",
  85. "content-type"
  86. ],
  87. "ExposeHeaders": ["ETag", "Location"]
  88. },
  89. {
  90. "AllowedOrigins": ["*"],
  91. "AllowedMethods": ["GET"],
  92. "MaxAgeSeconds": 3000
  93. }
  94. ]
  95. ```
  96. A good practice is to use two CORS rules: one for viewing the uploaded files,
  97. and one for uploading files. This is done above where the first object in the
  98. array defines the rules for uploading, and the second for viewing. The example
  99. above **makes files publicly viewable**. You can change it according to your
  100. needs.
  101. If you are using an IAM policy to allow access to the S3 bucket, the policy must
  102. have at least the `s3:PutObject` and `s3:PutObjectAcl` permissions scoped to the
  103. bucket in question. In-depth documentation about CORS rules is available on the
  104. [AWS documentation site](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html).
  105. ### Use with your own server
  106. The recommended approach is to integrate `@uppy/aws-s3` with your own server.
  107. You will need to do the following things:
  108. 1. [Setup a S3 bucket](#setting-up-your-s3-bucket).
  109. 2. [Setup your server](https://github.com/transloadit/uppy/blob/main/examples/aws-nodejs/index.js)
  110. 3. [Setup Uppy client](https://github.com/transloadit/uppy/blob/main/examples/aws-nodejs/public/index.html).
  111. ### Use with Companion
  112. [Companion](/docs/companion) has S3 routes built-in for a plug-and-play
  113. experience with Uppy.
  114. :::caution
  115. Generally it’s better for access control, observability, and scaling to
  116. integrate `@uppy/aws-s3` with your own server. You may want to use
  117. [Companion](/docs/companion) for creating, signing, and completing your S3
  118. uploads if you already need Companion for remote files (such as from Google
  119. Drive). Otherwise it’s not worth the hosting effort.
  120. :::
  121. ```js {10} showLineNumbers
  122. import Uppy from '@uppy/core';
  123. import Dashboard from '@uppy/dashboard';
  124. import AwsS3 from '@uppy/aws-s3';
  125. import '@uppy/core/dist/style.min.css';
  126. import '@uppy/dashboard/dist/style.min.css';
  127. const uppy = new Uppy()
  128. .use(Dashboard, { inline: true, target: 'body' })
  129. .use(AwsS3, {
  130. shouldUseMultipart: (file) => file.size > 100 * 2 ** 20,
  131. companionUrl: 'https://companion.uppy.io',
  132. });
  133. ```
  134. ## API
  135. ### Options
  136. #### `shouldUseMultipart(file)`
  137. :::warning
  138. Until the next major version, not setting this option uses the
  139. [legacy version of this plugin](../aws-s3/). This is a suboptimal experience for
  140. some of your user’s uploads. It’s best for speed and stability to upload large
  141. (100&nbsp;MiB+) files with multipart and small files with regular uploads.
  142. :::
  143. A boolean, or a function that returns a boolean which is called for each file
  144. that is uploaded with the corresponding `UppyFile` instance as argument.
  145. By default, all files are uploaded as multipart. In a future version, all files
  146. with a `file.size` ≤ 100&nbsp;MiB will be uploaded in a single chunk, all files
  147. larger than that as multipart.
  148. Here’s how to use it:
  149. ```js
  150. uppy.use(AwsS3, {
  151. shouldUseMultipart(file) {
  152. // Use multipart only for files larger than 100MiB.
  153. return file.size > 100 * 2 ** 20;
  154. },
  155. });
  156. ```
  157. #### `limit`
  158. The maximum amount of files to upload in parallel (`number`, default: `6`).
  159. Note that the amount of files is not the same as the amount of concurrent
  160. connections. Multipart uploads can use many requests per file. For example, for
  161. a 100 MiB file with a part size of 5 MiB:
  162. - 1 `createMultipartUpload` request
  163. - 100/5 = 20 sign requests (unless you are [signing on the client][])
  164. - 100/5 = 20 upload requests
  165. - 1 `completeMultipartUpload` request
  166. :::caution
  167. Unless you have a good reason and are well informed about the average internet
  168. speed of your users, do not set this higher. S3 uses HTTP/1.1, which means a
  169. limit to concurrent connections and your uploads may expire before they are
  170. uploaded.
  171. :::
  172. #### `companionUrl`
  173. URL to a [Companion](/docs/companion) instance (`string`, default: `null`).
  174. #### `companionHeaders`
  175. Custom headers that should be sent along to [Companion](/docs/companion) on
  176. every request (`Object`, default: `{}`).
  177. #### `companionCookiesRule`
  178. This option correlates to the
  179. [RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)
  180. (`string`, default: `'same-origin'`).
  181. This tells the plugin whether to send cookies to [Companion](/docs/companion).
  182. #### `retryDelays`
  183. `retryDelays` are the intervals in milliseconds used to retry a failed chunk
  184. (`array`, default: `[0, 1000, 3000, 5000]`).
  185. This is also used for [`signPart()`](#signpartfile-partdata). Set to `null` to
  186. disable automatic retries, and fail instantly if any chunk fails to upload.
  187. #### `getChunkSize(file)`
  188. A function that returns the minimum chunk size to use when uploading the given
  189. file.
  190. The S3 Multipart plugin uploads files in chunks. Chunks are sent in batches to
  191. have presigned URLs generated with [`signPart()`](#signpartfile-partdata). To
  192. reduce the amount of requests for large files, you can choose a larger chunk
  193. size, at the cost of having to re-upload more data if one chunk fails to upload.
  194. S3 requires a minimum chunk size of 5MiB, and supports at most 10,000 chunks per
  195. multipart upload. If `getChunkSize()` returns a size that’s too small, Uppy will
  196. increase it to S3’s minimum requirements.
  197. #### `getUploadParameters(file, options)`
  198. :::note
  199. When using [Companion][companion docs] to sign S3 uploads, you should not define
  200. this option.
  201. :::
  202. A function that will be called for each non-multipart upload.
  203. - `file`: `UppyFile` the file that will be uploaded
  204. - `options`: `object`
  205. - `signal`: `AbortSignal`
  206. - **Returns:** `object | Promise<object>`
  207. - `method`: `string`, the HTTP method to be used for the upload. This should
  208. be one of either `PUT` or `POST`, depending on the type of upload used.
  209. - `url`: `string`, the URL to which the upload request will be sent. When
  210. using a presigned PUT upload, this should be the URL to the S3 object with
  211. signing parameters included in the query string. When using a POST upload
  212. with a policy document, this should be the root URL of the bucket.
  213. - `fields` `object`, an object with form fields to send along with the upload
  214. request. For presigned PUT uploads (which are default), this should be left
  215. empty.
  216. - `headers`: `object`, an object with request headers to send along with the
  217. upload request. When using a presigned PUT upload, it’s a good idea to
  218. provide `headers['content-type']`. That will make sure that the request uses
  219. the same content-type that was used to generate the signature. Without it,
  220. the browser may decide on a different content-type instead, causing S3 to
  221. reject the upload.
  222. #### `createMultipartUpload(file)`
  223. A function that calls the S3 Multipart API to create a new upload.
  224. `file` is the file object from Uppy’s state. The most relevant keys are
  225. `file.name` and `file.type`.
  226. Return a Promise for an object with keys:
  227. - `uploadId` - The UploadID returned by S3.
  228. - `key` - The object key for the file. This needs to be returned to allow it to
  229. be different from the `file.name`.
  230. The default implementation calls out to Companion’s S3 signing endpoints.
  231. #### `listParts(file, { uploadId, key })`
  232. A function that calls the S3 Multipart API to list the parts of a file that have
  233. already been uploaded.
  234. Receives the `file` object from Uppy’s state, and an object with keys:
  235. - `uploadId` - The UploadID of this Multipart upload.
  236. - `key` - The object key of this Multipart upload.
  237. Return a Promise for an array of S3 Part objects, as returned by the S3
  238. Multipart API. Each object has keys:
  239. - `PartNumber` - The index in the file of the uploaded part.
  240. - `Size` - The size of the part in bytes.
  241. - `ETag` - The ETag of the part, used to identify it when completing the
  242. multipart upload and combining all parts into a single file.
  243. The default implementation calls out to Companion’s S3 signing endpoints.
  244. #### `signPart(file, partData)`
  245. A function that generates a signed URL for the specified part number. The
  246. `partData` argument is an object with the keys:
  247. - `uploadId` - The UploadID of this Multipart upload.
  248. - `key` - The object key in the S3 bucket.
  249. - `partNumber` - can’t be zero.
  250. - `body` – The data that will be signed.
  251. - `signal` – An `AbortSignal` that may be used to abort an ongoing request.
  252. This function should return a object, or a promise that resolves to an object,
  253. with the following keys:
  254. - `url` – the presigned URL, as a `string`.
  255. - `headers` – **(Optional)** Custom headers to send along with the request to S3
  256. endpoint.
  257. An example of what the return value should look like:
  258. ```json
  259. {
  260. "url": "https://bucket.region.amazonaws.com/path/to/file.jpg?partNumber=1&...",
  261. "headers": { "Content-MD5": "foo" }
  262. }
  263. ```
  264. #### `abortMultipartUpload(file, { uploadId, key })`
  265. A function that calls the S3 Multipart API to abort a Multipart upload, and
  266. removes all parts that have been uploaded so far.
  267. Receives the `file` object from Uppy’s state, and an object with keys:
  268. - `uploadId` - The UploadID of this Multipart upload.
  269. - `key` - The object key of this Multipart upload.
  270. This is typically called when the user cancels an upload. Cancellation cannot
  271. fail in Uppy, so the result of this function is ignored.
  272. The default implementation calls out to Companion’s S3 signing endpoints.
  273. #### `completeMultipartUpload(file, { uploadId, key, parts })`
  274. A function that calls the S3 Multipart API to complete a Multipart upload,
  275. combining all parts into a single object in the S3 bucket.
  276. Receives the `file` object from Uppy’s state, and an object with keys:
  277. - `uploadId` - The UploadID of this Multipart upload.
  278. - `key` - The object key of this Multipart upload.
  279. - `parts` - S3-style list of parts, an array of objects with `ETag` and
  280. `PartNumber` properties. This can be passed straight to S3’s Multipart API.
  281. Return a Promise for an object with properties:
  282. - `location` - **(Optional)** A publicly accessible URL to the object in the S3
  283. bucket.
  284. The default implementation calls out to Companion’s S3 signing endpoints.
  285. #### `allowedMetaFields: null`
  286. Pass an array of field names to limit the metadata fields that will be added to
  287. upload as query parameters.
  288. - Set this to `['name']` to only send the `name` field.
  289. - Set this to `null` (the default) to send _all_ metadata fields.
  290. - Set this to an empty array `[]` to not send any fields.
  291. <details>
  292. <summary>Deprecated options</summary>
  293. #### `prepareUploadParts(file, partData)`
  294. A function that generates a batch of signed URLs for the specified part numbers.
  295. Receives the `file` object from Uppy’s state. The `partData` argument is an
  296. object with keys:
  297. - `uploadId` - The UploadID of this Multipart upload.
  298. - `key` - The object key in the S3 bucket.
  299. - `parts` - An array of objects with the part number and chunk
  300. (`Array<{ number: number, chunk: blob }>`). `number` can’t be zero.
  301. `prepareUploadParts` should return a `Promise` with an `Object` with keys:
  302. - `presignedUrls` - A JavaScript object with the part numbers as keys and the
  303. presigned URL for each part as the value.
  304. - `headers` - **(Optional)** Custom headers to send along with every request per
  305. part (`{ 1: { 'Content-MD5': 'hash' }}`). These are (1-based) indexed by part
  306. number too so you can for instance send the MD5 hash validation for each part
  307. to S3.
  308. An example of what the return value should look like:
  309. ```json
  310. {
  311. "presignedUrls": {
  312. "1": "https://bucket.region.amazonaws.com/path/to/file.jpg?partNumber=1&...",
  313. "2": "https://bucket.region.amazonaws.com/path/to/file.jpg?partNumber=2&...",
  314. "3": "https://bucket.region.amazonaws.com/path/to/file.jpg?partNumber=3&..."
  315. },
  316. "headers": {
  317. "1": { "Content-MD5": "foo" },
  318. "2": { "Content-MD5": "bar" },
  319. "3": { "Content-MD5": "baz" }
  320. }
  321. }
  322. ```
  323. If an error occurred, reject the `Promise` with an `Object` with the following
  324. keys:
  325. ```json
  326. { "source": { "status": 500 } }
  327. ```
  328. `status` is the HTTP code and is required for determining whether to retry the
  329. request. `prepareUploadParts` will be retried if the code is `0`, `409`, `423`,
  330. or between `500` and `600`.
  331. </details>
  332. #### `getTemporarySecurityCredentials(options)`
  333. :::note
  334. When using [Companion][companion docs] as a backend, you can pass `true` instead
  335. of a function. Setting up Companion will not simplify the process of getting
  336. signing on the client.
  337. :::
  338. A boolean (when using Companion), or an (async) function to retrieve temporary
  339. security credentials used for all uploads instead of signing every part. This
  340. results in less request overhead which can lead to around 20% faster uploads.
  341. This is a security tradeoff. We recommend to not use this option unless you are
  342. familiar with the security implications of temporary credentials, and how to
  343. setup your bucket to make it work. See the
  344. [Requesting temporary security credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html)
  345. AWS guide for more information.
  346. It’s strongly recommended to have some sort of caching process to avoid
  347. requesting more temporary token than necessary.
  348. - `options`: `object`
  349. - `signal`: `AbortSignal`
  350. - **Returns:** `object | Promise<object>`
  351. - `credentials`: `object`
  352. - `AccessKeyId`: `string`
  353. - `SecretAccessKey`: `string`
  354. - `SessionToken`: `string`
  355. - `Expiration`: `string`
  356. - `bucket`: `string`
  357. - `region`: `string`
  358. If you are using Companion (for example because you want to support remote
  359. upload sources), you can pass a boolean:
  360. ```js
  361. uppy.use(AwsS3, {
  362. // This is an example using Companion:
  363. companionUrl: 'http://companion.uppy.io',
  364. getTemporarySecurityCredentials: true,
  365. shouldUseMultipart: (file) => file.size > 100 * 2 ** 20,
  366. });
  367. ```
  368. In the most common case, you are using a different backend, in which case you
  369. need to specify a function:
  370. ```js
  371. uppy.use(AwsS3, {
  372. // This is an example not using Companion:
  373. async getTemporarySecurityCredentials({ signal }) {
  374. const response = await fetch('/sts-token', { signal });
  375. if (!response.ok)
  376. throw new Error('Failed to fetch STS', { cause: response });
  377. return response.json();
  378. },
  379. shouldUseMultipart: (file) => file.size > 100 * 2 ** 20,
  380. });
  381. ```
  382. [`gettemporarysecuritycredentials`]: #gettemporarysecuritycredentialsoptions
  383. [`shouldusemultipart`]: #shouldusemultipartfile
  384. [companion docs]: /docs/companion
  385. [signing on the client]: #gettemporarysecuritycredentialsoptions