mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-17 16:50:22 +00:00
@uppy/aws-s3: add endpoint option (#5173)
This commit is contained in:
parent
9421cea0fb
commit
087d8f07f0
10 changed files with 642 additions and 494 deletions
267
examples/aws-nodejs/public/withCustomEndpoints.html
Normal file
267
examples/aws-nodejs/public/withCustomEndpoints.html
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Uppy – AWS upload example</title>
|
||||
<link href="./uppy.min.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>AWS upload example</h1>
|
||||
<div id="uppy"></div>
|
||||
<footer>
|
||||
You seeing the complex example, with a backend that does not mimick a
|
||||
Companion-like instance. See
|
||||
<a href="./index.html">the simplified example</a> if you don't need custom
|
||||
functions for handling communication with the backend.
|
||||
</footer>
|
||||
<noscript>You need JavaScript to run this example.</noscript>
|
||||
<script type="module">
|
||||
import { Uppy, Dashboard, AwsS3 } from './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('/s3/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('/s3/sign', {
|
||||
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) {
|
||||
signal?.throwIfAborted()
|
||||
|
||||
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
|
||||
|
||||
signal?.throwIfAborted()
|
||||
|
||||
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) {
|
||||
signal?.throwIfAborted()
|
||||
|
||||
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,
|
||||
) {
|
||||
signal?.throwIfAborted()
|
||||
|
||||
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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue