123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- <!doctype html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>Uppy – AWS upload example</title>
- <link href="https://releases.transloadit.com/uppy/v3.14.0/uppy.min.css" rel="stylesheet">
- </head>
- <body>
- <h1>AWS upload example</h1>
- <div id="uppy"></div>
- <script type="module">
- import { Uppy, Dashboard, AwsS3 } from "https://releases.transloadit.com/uppy/v3.14.0/uppy.min.mjs"
- /**
- * This generator transforms a deep object into URL-encodable pairs
- * to work with `URLSearchParams` on the client and `body-parser` on the server.
- */
- function* serializeSubPart(key, value) {
- if (typeof value !== "object") {
- yield [key, value];
- return;
- }
- if (Array.isArray(value)) {
- for (const val of value) {
- yield* serializeSubPart(`${key}[]`, val);
- }
- return;
- }
- for (const [subkey, val] of Object.entries(value)) {
- yield* serializeSubPart(key ? `${key}[${subkey}]` : subkey, val);
- }
- }
- function serialize(data) {
- // If you want to avoid preflight requests, use URL-encoded syntax:
- return new URLSearchParams(serializeSubPart(null, data))
- // If you don't care about additional preflight requests, you can also use:
- // return JSON.stringify(data)
- // You'd also have to add `Content-Type` header with value `application/json`.
- }
- {
- const MiB = 0x10_00_00;
- const uppy = new Uppy()
- .use(Dashboard, {
- inline: true,
- target: '#uppy',
- })
- .use(AwsS3, {
- id: 'myAWSPlugin',
- // Files that are more than 100MiB should be uploaded in multiple parts.
- shouldUseMultipart: (file) => file.size > 100 * MiB,
- /**
- * This method tells Uppy how to retrieve a temporary token for signing on the client.
- * Signing on the client is optional, you can also do the signing from the server.
- */
- async getTemporarySecurityCredentials({signal}) {
- const response = await fetch('/sts', { signal })
- if (!response.ok) throw new Error('Unsuccessful request', { cause: response })
- return response.json()
- },
- // ========== Non-Multipart Uploads ==========
- /**
- * This method tells Uppy how to handle non-multipart uploads.
- * If for some reason you want to only support multipart uploads,
- * you don't need to implement it.
- */
- async getUploadParameters (file, options) {
- if (typeof crypto?.subtle === 'object') {
- // If WebCrypto is available, let's do signing from the client.
- return uppy.getPlugin('myAWSPlugin').createSignedURL(file, options)
- }
- // Send a request to our Express.js signing endpoint.
- const response = await fetch('/sign-s3', {
- method: 'POST',
- headers: {
- accept: 'application/json',
- },
- body: serialize({
- filename: file.name,
- contentType: file.type,
- }),
- signal: options.signal,
- })
- if (!response.ok) throw new Error('Unsuccessful request', { cause: response })
- // Parse the JSON response.
- const data = await response.json()
- // Return an object in the correct shape.
- return {
- method: data.method,
- url: data.url,
- fields: {}, // For presigned PUT uploads, this should be left empty.
- // Provide content type header required by S3
- headers: {
- 'Content-Type': file.type,
- },
- }
- },
- // ========== Multipart Uploads ==========
- // The following methods are only useful for multipart uploads:
- // If you are not interested in multipart uploads, you don't need to
- // implement them (you'd also need to set `shouldUseMultipart: false` though).
- async createMultipartUpload(file, signal) {
- if (signal?.aborted) {
- const err = new DOMException('The operation was aborted', 'AbortError')
- Object.defineProperty(err, 'cause', { __proto__: null, configurable: true, writable: true, value: signal.reason })
- throw err
- }
- const metadata = {}
- Object.keys(file.meta || {}).forEach(key => {
- if (file.meta[key] != null) {
- metadata[key] = file.meta[key].toString()
- }
- })
- const response = await fetch('/s3/multipart', {
- method: 'POST',
- // Send and receive JSON.
- headers: {
- accept: 'application/json',
- },
- body: serialize({
- filename: file.name,
- type: file.type,
- metadata,
- }),
- signal,
- })
- if (!response.ok) throw new Error('Unsuccessful request', { cause: response })
- // Parse the JSON response.
- const data = await response.json()
- return data
- },
- async abortMultipartUpload (file, { key, uploadId }, signal) {
- const filename = encodeURIComponent(key)
- const uploadIdEnc = encodeURIComponent(uploadId)
- const response = await fetch(`/s3/multipart/${uploadIdEnc}?key=${filename}`, {
- method: 'DELETE',
- signal,
- })
-
- if (!response.ok) throw new Error('Unsuccessful request', { cause: response })
- },
- async signPart (file, options) {
- if (typeof crypto?.subtle === 'object') {
- // If WebCrypto, let's do signing from the client.
- return uppy.getPlugin('myAWSPlugin').createSignedURL(file, options)
- }
- const { uploadId, key, partNumber, signal } = options
- if (signal?.aborted) {
- const err = new DOMException('The operation was aborted', 'AbortError')
- Object.defineProperty(err, 'cause', { __proto__: null, configurable: true, writable: true, value: signal.reason })
- throw err
- }
- if (uploadId == null || key == null || partNumber == null) {
- throw new Error('Cannot sign without a key, an uploadId, and a partNumber')
- }
- const filename = encodeURIComponent(key)
- const response = await fetch(`/s3/multipart/${uploadId}/${partNumber}?key=${filename}`, { signal })
- if (!response.ok) throw new Error('Unsuccessful request', { cause: response })
- const data = await response.json()
- return data
- },
- async listParts (file, { key, uploadId }, signal) {
- if (signal?.aborted) {
- const err = new DOMException('The operation was aborted', 'AbortError')
- Object.defineProperty(err, 'cause', { __proto__: null, configurable: true, writable: true, value: signal.reason })
- throw err
- }
- const filename = encodeURIComponent(key)
- const response = await fetch(`/s3/multipart/${uploadId}?key=${filename}`, { signal })
- if (!response.ok) throw new Error('Unsuccessful request', { cause: response })
- const data = await response.json()
- return data
- },
- async completeMultipartUpload (file, { key, uploadId, parts }, signal) {
- if (signal?.aborted) {
- const err = new DOMException('The operation was aborted', 'AbortError')
- Object.defineProperty(err, 'cause', { __proto__: null, configurable: true, writable: true, value: signal.reason })
- throw err
- }
- const filename = encodeURIComponent(key)
- const uploadIdEnc = encodeURIComponent(uploadId)
- const response = await fetch(`s3/multipart/${uploadIdEnc}/complete?key=${filename}`, {
- method: 'POST',
- headers: {
- accept: 'application/json',
- },
- body: serialize({ parts }),
- signal,
- })
- if (!response.ok) throw new Error('Unsuccessful request', { cause: response })
- const data = await response.json()
- return data
- },
- })
-
- uppy.on('complete', (result) => {
- console.log('Upload complete! We’ve uploaded these files:', result.successful)
- })
-
- uppy.on('upload-success', (file, data) => {
- console.log('Upload success! We’ve uploaded this file:', file.meta['name'])
- })
- }
- </script>
- </body>
- </html>
|