mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-17 16:50:22 +00:00
commit
ef477ff258
10 changed files with 184 additions and 57 deletions
|
|
@ -47,6 +47,7 @@ Ideas that will be planned and find their way into a release at one point
|
|||
- [ ] possibility to edit/delete more than one file at once #118, #97
|
||||
- [ ] optimize problematic filenames #72
|
||||
- [ ] an uploader plugin to receive files in a callback instead of uploading them
|
||||
- [ ] core: calling `upload` immediately after `addFile` does not upload all files (#249 @goto-bus-stop)
|
||||
|
||||
## 1.0 Goals
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ What we need to do to release Uppy 1.0
|
|||
- [x] feature: restrictions: by size, number of files, file type
|
||||
- [x] feature: beta file recovering after closed tab / browser crash
|
||||
- [ ] feature: improved UI for Provider, Google Drive and Instagram, grid/list views
|
||||
- [ ] feature: finish the direct-to-s3 upload plugin and test it with the flow to then upload to :transloadit: afterwards. This is because this might influence the inner flow of the plugin architecture quite a bit
|
||||
- [x] feature: finish the direct-to-s3 upload plugin and test it with the flow to then upload to :transloadit: afterwards. This is because this might influence the inner flow of the plugin architecture quite a bit
|
||||
- [ ] feature: Uppy should work well with React/Redux and React Native
|
||||
- [ ] feature: preset for Transloadit that mimics jQuery SDK
|
||||
- [ ] QA: test how everything works together: user experience from `npm install` to production build with Webpack, using in React/Redux environment (npm pack)
|
||||
|
|
@ -95,9 +96,8 @@ Theme: React and Retry
|
|||
|
||||
- [ ] core: add error in file progress state? error UI, question mark button, `core:error` (@arturi)
|
||||
- [ ] core: retry or show error when upload can’t start / fails (offline, wrong endpoint) — now it just sits there (@arturi @goto-bus-stop)
|
||||
- [ ] core: calling `upload` immediately after `addFile` does not upload all files (#249 @goto-bus-stop)
|
||||
- [ ] core: React / Redux PRs (@arturi @goto-bus-stop)
|
||||
- [ ] transloadit: upload to S3, then import into :tl: assembly using `/add_file?s3url=${url}` (@goto-bus-stop)
|
||||
- [x] transloadit: upload to S3, then import into :tl: assembly using `/add_file?s3url=${url}` (@goto-bus-stop)
|
||||
- [ ] goldenretriver: add “ghost” files (@arturi @goto-bus-stop)
|
||||
- [ ] dashboard: cancel button for any kind of uploads? currently resume/pause only for tus, and cancel for XHR (@arturi @goto-bus-stop)
|
||||
- [x] informer: support “explanations”, a (?) button that shows more info on hover / click
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ class Uppy {
|
|||
this.initSocket = this.initSocket.bind(this)
|
||||
this.log = this.log.bind(this)
|
||||
this.addFile = this.addFile.bind(this)
|
||||
this.removeFile = this.removeFile.bind(this)
|
||||
this.calculateProgress = this.calculateProgress.bind(this)
|
||||
this.resetProgress = this.resetProgress.bind(this)
|
||||
|
||||
|
|
@ -115,10 +116,8 @@ class Uppy {
|
|||
*
|
||||
*/
|
||||
updateAll (state) {
|
||||
Object.keys(this.plugins).forEach((pluginType) => {
|
||||
this.plugins[pluginType].forEach((plugin) => {
|
||||
plugin.update(state)
|
||||
})
|
||||
this.iteratePlugins(plugin => {
|
||||
plugin.update(state)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -444,7 +443,11 @@ class Uppy {
|
|||
|
||||
this.on('core:upload-error', (fileID, error) => {
|
||||
const fileName = this.state.files[fileID].name
|
||||
this.info(`Failed to upload: ${fileName}`, 'error', 5000)
|
||||
let message = `Failed to upload ${fileName}`
|
||||
if (typeof error === 'object' && error.message) {
|
||||
message = `${message}: ${error.message}`
|
||||
}
|
||||
this.info(message, 'error', 5000)
|
||||
})
|
||||
|
||||
this.on('core:upload', () => {
|
||||
|
|
@ -609,21 +612,21 @@ class Uppy {
|
|||
|
||||
// Instantiate
|
||||
const plugin = new Plugin(this, opts)
|
||||
const pluginName = plugin.id
|
||||
const pluginId = plugin.id
|
||||
this.plugins[plugin.type] = this.plugins[plugin.type] || []
|
||||
|
||||
if (!pluginName) {
|
||||
throw new Error('Your plugin must have a name')
|
||||
if (!pluginId) {
|
||||
throw new Error('Your plugin must have an id')
|
||||
}
|
||||
|
||||
if (!plugin.type) {
|
||||
throw new Error('Your plugin must have a type')
|
||||
}
|
||||
|
||||
let existsPluginAlready = this.getPlugin(pluginName)
|
||||
let existsPluginAlready = this.getPlugin(pluginId)
|
||||
if (existsPluginAlready) {
|
||||
let msg = `Already found a plugin named '${existsPluginAlready.name}'.
|
||||
Tried to use: '${pluginName}'.
|
||||
let msg = `Already found a plugin named '${existsPluginAlready.id}'.
|
||||
Tried to use: '${pluginId}'.
|
||||
Uppy is currently limited to running one of every plugin.
|
||||
Share your use case with us over at
|
||||
https://github.com/transloadit/uppy/issues/
|
||||
|
|
|
|||
|
|
@ -66,6 +66,14 @@ module.exports = class AwsS3 extends Plugin {
|
|||
key: getValue('Key'),
|
||||
etag: getValue('ETag')
|
||||
}
|
||||
},
|
||||
getResponseError (xhr) {
|
||||
// If no response, we don't have a specific error message, use the default.
|
||||
if (!xhr.responseXML) {
|
||||
return
|
||||
}
|
||||
const error = xhr.responseXML.querySelector('Error > Message')
|
||||
return new Error(error.textContent)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -6,9 +6,13 @@ module.exports = function fileCard (props) {
|
|||
const file = props.fileCardFor ? props.files[props.fileCardFor] : false
|
||||
const meta = {}
|
||||
|
||||
function tempStoreMeta (ev) {
|
||||
const tempStoreMetaOrSubmit = (ev) => {
|
||||
if (ev.keyCode === 13) {
|
||||
props.done(meta, file.id)
|
||||
}
|
||||
|
||||
const value = ev.target.value
|
||||
const name = ev.target.attributes.name.value
|
||||
const name = ev.target.dataset.name
|
||||
meta[name] = value
|
||||
}
|
||||
|
||||
|
|
@ -18,11 +22,11 @@ module.exports = function fileCard (props) {
|
|||
return html`<fieldset class="UppyDashboardFileCard-fieldset">
|
||||
<label class="UppyDashboardFileCard-label">${field.name}</label>
|
||||
<input class="UppyDashboardFileCard-input"
|
||||
name="${field.id}"
|
||||
type="text"
|
||||
data-name="${field.id}"
|
||||
value="${file.meta[field.id]}"
|
||||
placeholder="${field.placeholder || ''}"
|
||||
onkeyup=${tempStoreMeta} /></fieldset>`
|
||||
onkeyup=${tempStoreMetaOrSubmit} /></fieldset>`
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -46,8 +50,8 @@ module.exports = function fileCard (props) {
|
|||
<div class="UppyDashboardFileCard-info">
|
||||
<fieldset class="UppyDashboardFileCard-fieldset">
|
||||
<label class="UppyDashboardFileCard-label">Name</label>
|
||||
<input class="UppyDashboardFileCard-input" name="name" type="text" value="${file.meta.name}"
|
||||
onkeyup=${tempStoreMeta} />
|
||||
<input class="UppyDashboardFileCard-input" data-name="name" type="text" value="${file.meta.name}"
|
||||
onkeyup=${tempStoreMetaOrSubmit} />
|
||||
</fieldset>
|
||||
${renderMetaFields(file)}
|
||||
</div>
|
||||
|
|
@ -60,5 +64,5 @@ module.exports = function fileCard (props) {
|
|||
title="Finish editing file"
|
||||
onclick=${() => props.done(meta, file.id)}>${checkIcon()}</button>
|
||||
</div>
|
||||
</div>`
|
||||
</div>`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ module.exports = class DashboardUI extends Plugin {
|
|||
}
|
||||
|
||||
handleFileCard (fileId) {
|
||||
const modal = this.core.getState().modal
|
||||
const modal = this.core.state.modal
|
||||
|
||||
this.core.setState({
|
||||
modal: Object.assign({}, modal, {
|
||||
|
|
@ -318,44 +318,36 @@ module.exports = class DashboardUI extends Plugin {
|
|||
return target.type === 'progressindicator'
|
||||
})
|
||||
|
||||
// const addFile = (file) => {
|
||||
// this.core.emitter.emit('core:file-add', file)
|
||||
// }
|
||||
|
||||
const removeFile = (fileID) => {
|
||||
this.core.emitter.emit('core:file-remove', fileID)
|
||||
}
|
||||
|
||||
const startUpload = (ev) => {
|
||||
this.core.upload().catch((err) => {
|
||||
// Log error.
|
||||
console.error(err.stack || err.message || err)
|
||||
this.core.log(err.stack || err.message || err)
|
||||
})
|
||||
}
|
||||
|
||||
const pauseUpload = (fileID) => {
|
||||
this.core.emitter.emit('core:upload-pause', fileID)
|
||||
this.core.emit.emit('core:upload-pause', fileID)
|
||||
}
|
||||
|
||||
const cancelUpload = (fileID) => {
|
||||
this.core.emitter.emit('core:upload-cancel', fileID)
|
||||
this.core.emitter.emit('core:file-remove', fileID)
|
||||
this.core.emit('core:upload-cancel', fileID)
|
||||
this.core.emit('core:file-remove', fileID)
|
||||
}
|
||||
|
||||
const showFileCard = (fileID) => {
|
||||
this.core.emitter.emit('dashboard:file-card', fileID)
|
||||
this.core.emit('dashboard:file-card', fileID)
|
||||
}
|
||||
|
||||
const fileCardDone = (meta, fileID) => {
|
||||
this.core.emitter.emit('core:update-meta', meta, fileID)
|
||||
this.core.emitter.emit('dashboard:file-card')
|
||||
this.core.emit('core:update-meta', meta, fileID)
|
||||
this.core.emit('dashboard:file-card')
|
||||
}
|
||||
|
||||
const info = (text, type, duration) => {
|
||||
this.core.info(text, type, duration)
|
||||
}
|
||||
|
||||
const resumableUploads = this.core.getState().capabilities.resumableUploads || false
|
||||
const resumableUploads = this.core.state.capabilities.resumableUploads || false
|
||||
|
||||
return Dashboard({
|
||||
state: state,
|
||||
|
|
@ -382,7 +374,7 @@ module.exports = class DashboardUI extends Plugin {
|
|||
pauseAll: this.pauseAll,
|
||||
resumeAll: this.resumeAll,
|
||||
addFile: this.core.addFile,
|
||||
removeFile: removeFile,
|
||||
removeFile: this.core.removeFile,
|
||||
info: info,
|
||||
note: this.opts.note,
|
||||
metaFields: state.metaFields,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ module.exports = class Plugin {
|
|||
constructor (core, opts) {
|
||||
this.core = core
|
||||
this.opts = opts || {}
|
||||
this.type = 'none'
|
||||
|
||||
// clear everything inside the target selector
|
||||
this.opts.replaceTargetContent === this.opts.replaceTargetContent || true
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ module.exports = class Client {
|
|||
Object.keys(fields).forEach((key) => {
|
||||
data.append(key, fields[key])
|
||||
})
|
||||
data.append('tus_num_expected_upload_files', expectedFiles)
|
||||
data.append('num_expected_upload_files', expectedFiles)
|
||||
|
||||
return fetch(`${this.apiUrl}/assemblies`, {
|
||||
method: 'post',
|
||||
|
|
@ -47,6 +47,26 @@ module.exports = class Client {
|
|||
})
|
||||
}
|
||||
|
||||
reserveFile (assembly, file) {
|
||||
const size = encodeURIComponent(file.size)
|
||||
return fetch(`${assembly.assembly_ssl_url}/reserve_file?size=${size}`, { method: 'post' })
|
||||
.then((response) => response.json())
|
||||
}
|
||||
|
||||
addFile (assembly, file) {
|
||||
if (!file.uploadURL) {
|
||||
return Promise.reject(new Error('File does not have an `uploadURL`.'))
|
||||
}
|
||||
const size = encodeURIComponent(file.size)
|
||||
const url = encodeURIComponent(file.uploadURL)
|
||||
const filename = encodeURIComponent(file.name)
|
||||
const fieldname = 'file'
|
||||
|
||||
const qs = `size=${size}&filename=${filename}&fieldname=${fieldname}&s3Url=${url}`
|
||||
return fetch(`${assembly.assembly_ssl_url}/add_file?${qs}`, { method: 'post' })
|
||||
.then((response) => response.json())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current status for an assembly.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ module.exports = class Transloadit extends Plugin {
|
|||
waitForEncoding: false,
|
||||
waitForMetadata: false,
|
||||
alwaysRunAssembly: false, // TODO name
|
||||
importFromUploadURLs: false,
|
||||
signature: null,
|
||||
params: null,
|
||||
fields: {},
|
||||
|
|
@ -44,6 +45,7 @@ module.exports = class Transloadit extends Plugin {
|
|||
|
||||
this.prepareUpload = this.prepareUpload.bind(this)
|
||||
this.afterUpload = this.afterUpload.bind(this)
|
||||
this.onFileUploadURLAvailable = this.onFileUploadURLAvailable.bind(this)
|
||||
|
||||
if (this.opts.params) {
|
||||
this.validateParams(this.opts.params)
|
||||
|
|
@ -111,6 +113,8 @@ module.exports = class Transloadit extends Plugin {
|
|||
}
|
||||
|
||||
createAssembly (fileIDs, uploadID, options) {
|
||||
const pluginOptions = this.opts
|
||||
|
||||
this.core.log('Transloadit: create assembly')
|
||||
|
||||
return this.client.createAssembly({
|
||||
|
|
@ -136,25 +140,28 @@ module.exports = class Transloadit extends Plugin {
|
|||
// Attach meta parameters for the Tus plugin. See:
|
||||
// https://github.com/tus/tusd/wiki/Uploading-to-Transloadit-using-tus#uploading-using-tus
|
||||
// TODO Should this `meta` be moved to a `tus.meta` property instead?
|
||||
// If the MetaData plugin can add eg. resize parameters, it doesn't
|
||||
// make much sense to set those as upload-metadata for tus.
|
||||
const meta = Object.assign({}, file.meta, {
|
||||
const tlMeta = {
|
||||
assembly_url: assembly.assembly_url,
|
||||
filename: file.name,
|
||||
fieldname: 'file'
|
||||
})
|
||||
}
|
||||
const meta = Object.assign({}, file.meta, tlMeta)
|
||||
// Add assembly-specific Tus endpoint.
|
||||
const tus = Object.assign({}, file.tus, {
|
||||
endpoint: assembly.tus_url
|
||||
endpoint: assembly.tus_url,
|
||||
// Only send assembly metadata to the tus endpoint.
|
||||
metaFields: Object.keys(tlMeta)
|
||||
})
|
||||
const transloadit = {
|
||||
assembly: assembly.assembly_id
|
||||
}
|
||||
return Object.assign(
|
||||
{},
|
||||
file,
|
||||
{ meta, tus, transloadit }
|
||||
)
|
||||
|
||||
const newFile = Object.assign({}, file, { transloadit })
|
||||
// Only configure the Tus plugin if we are uploading straight to Transloadit (the default).
|
||||
if (!pluginOptions.importFromUploadURLs) {
|
||||
Object.assign(newFile, { meta, tus })
|
||||
}
|
||||
return newFile
|
||||
}
|
||||
|
||||
const files = Object.assign({}, this.core.state.files)
|
||||
|
|
@ -167,10 +174,12 @@ module.exports = class Transloadit extends Plugin {
|
|||
this.core.emit('transloadit:assembly-created', assembly, fileIDs)
|
||||
|
||||
return this.connectSocket(assembly)
|
||||
}).then(() => {
|
||||
.then(() => assembly)
|
||||
}).then((assembly) => {
|
||||
this.core.log('Transloadit: Created assembly')
|
||||
return assembly
|
||||
}).catch((err) => {
|
||||
this.core.info(this.opts.locale.strings.creatingAssemblyFailed, 'error', 0)
|
||||
this.core.info(pluginOptions.locale.strings.creatingAssemblyFailed, 'error', 0)
|
||||
|
||||
// Reject the promise.
|
||||
throw err
|
||||
|
|
@ -181,6 +190,35 @@ module.exports = class Transloadit extends Plugin {
|
|||
return this.opts.waitForEncoding || this.opts.waitForMetadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when `importFromUploadURLs` is enabled: reserves all files in
|
||||
* the assembly.
|
||||
*/
|
||||
reserveFiles (assembly, fileIDs) {
|
||||
return Promise.all(fileIDs.map((fileID) => {
|
||||
const file = this.core.getFile(fileID)
|
||||
return this.client.reserveFile(assembly, file)
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when `importFromUploadURLs` is enabled: adds files to the assembly
|
||||
* once they have been fully uploaded.
|
||||
*/
|
||||
onFileUploadURLAvailable (fileID) {
|
||||
const file = this.core.getFile(fileID)
|
||||
if (!file || !file.transloadit || !file.transloadit.assembly) {
|
||||
return
|
||||
}
|
||||
|
||||
const assembly = this.state.assemblies[file.transloadit.assembly]
|
||||
|
||||
this.client.addFile(assembly, file).catch((err) => {
|
||||
this.core.log(err)
|
||||
this.core.emit('transloadit:import-error', assembly, file.id, err)
|
||||
})
|
||||
}
|
||||
|
||||
findFile (uploadedFile) {
|
||||
const files = this.core.state.files
|
||||
for (const id in files) {
|
||||
|
|
@ -272,7 +310,11 @@ module.exports = class Transloadit extends Plugin {
|
|||
})
|
||||
|
||||
const createAssembly = ({ fileIDs, options }) => {
|
||||
return this.createAssembly(fileIDs, uploadID, options).then(() => {
|
||||
return this.createAssembly(fileIDs, uploadID, options).then((assembly) => {
|
||||
if (this.opts.importFromUploadURLs) {
|
||||
return this.reserveFiles(assembly, fileIDs)
|
||||
}
|
||||
}).then(() => {
|
||||
fileIDs.forEach((fileID) => {
|
||||
this.core.emit('core:preprocess-complete', fileID)
|
||||
})
|
||||
|
|
@ -382,13 +424,28 @@ module.exports = class Transloadit extends Plugin {
|
|||
reject(error)
|
||||
}
|
||||
|
||||
const onImportError = (assembly, fileID, error) => {
|
||||
if (assemblyIDs.indexOf(assembly.assembly_id) === -1) {
|
||||
return
|
||||
}
|
||||
|
||||
// Not sure if we should be doing something when it's just one file failing.
|
||||
// ATM, the only options are 1) ignoring or 2) failing the entire upload.
|
||||
// I think failing the upload is better than silently ignoring.
|
||||
// In the future we should maybe have a way to resolve uploads with some failures,
|
||||
// like returning an object with `{ successful, failed }` uploads.
|
||||
onAssemblyError(assembly, error)
|
||||
}
|
||||
|
||||
const removeListeners = () => {
|
||||
this.core.off('transloadit:complete', onAssemblyFinished)
|
||||
this.core.off('transloadit:assembly-error', onAssemblyError)
|
||||
this.core.off('transloadit:import-error', onImportError)
|
||||
}
|
||||
|
||||
this.core.on('transloadit:complete', onAssemblyFinished)
|
||||
this.core.on('transloadit:assembly-error', onAssemblyError)
|
||||
this.core.on('transloadit:import-error', onImportError)
|
||||
}).then(() => {
|
||||
// Clean up uploadID → assemblyIDs, they're no longer going to be used anywhere.
|
||||
const uploadsAssemblies = Object.assign({}, this.state.uploadsAssemblies)
|
||||
|
|
@ -401,6 +458,10 @@ module.exports = class Transloadit extends Plugin {
|
|||
this.core.addPreProcessor(this.prepareUpload)
|
||||
this.core.addPostProcessor(this.afterUpload)
|
||||
|
||||
if (this.opts.importFromUploadURLs) {
|
||||
this.core.on('core:upload-success', this.onFileUploadURLAvailable)
|
||||
}
|
||||
|
||||
this.updateState({
|
||||
// Contains assembly status objects, indexed by their ID.
|
||||
assemblies: {},
|
||||
|
|
@ -416,6 +477,10 @@ module.exports = class Transloadit extends Plugin {
|
|||
uninstall () {
|
||||
this.core.removePreProcessor(this.prepareUpload)
|
||||
this.core.removePostProcessor(this.afterUpload)
|
||||
|
||||
if (this.opts.importFromUploadURLs) {
|
||||
this.core.off('core:upload-success', this.onFileUploadURLAvailable)
|
||||
}
|
||||
}
|
||||
|
||||
getAssembly (id) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ module.exports = class XHRUpload extends Plugin {
|
|||
headers: {},
|
||||
getResponseData (xhr) {
|
||||
return JSON.parse(xhr.response)
|
||||
},
|
||||
getResponseError (xhr) {
|
||||
return new Error('Upload error')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,8 +91,10 @@ module.exports = class XHRUpload extends Plugin {
|
|||
|
||||
return resolve(file)
|
||||
} else {
|
||||
this.core.emit('core:upload-error', file.id, xhr)
|
||||
return reject('Upload error')
|
||||
const error = opts.getResponseError(xhr) || new Error('Upload error')
|
||||
error.request = xhr
|
||||
this.core.emit('core:upload-error', file.id, error)
|
||||
return reject(error)
|
||||
}
|
||||
|
||||
// var upload = {}
|
||||
|
|
@ -103,7 +108,7 @@ module.exports = class XHRUpload extends Plugin {
|
|||
|
||||
xhr.addEventListener('error', (ev) => {
|
||||
this.core.emit('core:upload-error', file.id)
|
||||
return reject('Upload error')
|
||||
return reject(new Error('Upload error'))
|
||||
})
|
||||
|
||||
xhr.open(opts.method.toUpperCase(), opts.endpoint, true)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ uppy.use(Transloadit, {
|
|||
})
|
||||
```
|
||||
|
||||
NB: It is not required to use the `Tus10` plugin if [importFromUploadURLs](#importFromUploadURLs) is enabled.
|
||||
|
||||
## Options
|
||||
|
||||
### `waitForEncoding`
|
||||
|
|
@ -31,6 +33,35 @@ Whether to wait for all assemblies to complete before completing the upload.
|
|||
Whether to wait for metadata to be extracted from uploaded files before completing the upload.
|
||||
If `waitForEncoding` is enabled, this has no effect.
|
||||
|
||||
### `importFromUploadURLs`
|
||||
|
||||
Instead of uploading to Transloadit's servers directly, allow another plugin to upload files, and then import those files into the Transloadit assembly.
|
||||
Default `false`.
|
||||
|
||||
When enabling this option, Transloadit will *not* configure the Tus plugin to upload to Transloadit.
|
||||
Instead, a separate upload plugin must be used.
|
||||
Once the upload completes, the Transloadit plugin adds the uploaded file to the assembly.
|
||||
|
||||
For example, to upload files to an S3 bucket and then transcode them:
|
||||
|
||||
```js
|
||||
uppy.use(AwsS3, {
|
||||
getUploadParameters (file) {
|
||||
return { /* upload parameters */ }
|
||||
}
|
||||
})
|
||||
uppy.use(Transloadit, {
|
||||
importFromUploadURLs: true,
|
||||
params: {
|
||||
auth: { key: /* secret */ },
|
||||
template_id: /* secret */
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
In order for this to work, the upload plugin must assign a publically accessible `uploadURL` property to the uploaded file object.
|
||||
The Tus and S3 plugins both do this—for the XHRUpload plugin, you may have to specify a custom `getUploadResponse` function.
|
||||
|
||||
### `params`
|
||||
|
||||
The assembly parameters to use for the upload.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue