Merge branch 'master' into server-error

This commit is contained in:
Kevin van Zonneveld 2017-07-25 13:58:01 +02:00 committed by GitHub
commit 2398d1c574
14 changed files with 4435 additions and 622 deletions

View file

@ -40,6 +40,8 @@ Ideas that will be planned and find their way into a release at one point
- [ ] uppy/uppy-server: review websocket connection and throttling progress events (@arturi, @ifedapoolarewaju)
- [ ] uploaders: consider not showing progress updates from the server after an uploads been paused (@arturi, @ifedapoolarewaju)
- [ ] image cropping on the client (#151)
- [ ] maybe restrict system file picking dialog too https://github.com/transloadit/uppy/issues/253
- [ ] uppy-server: what happens if access token expires amid an upload/download process.
## 1.0 Goals
@ -66,14 +68,19 @@ What we need to do to release Uppy 1.0
- [ ] uppy-server: better error handling, general cleanup (remove unused code. etc)
- [ ] uppy-server: security audit
## 0.19.0
- [ ] allow minimizing the Dashboard during upload (@arturi)
- [ ] webcam: look into simplifying / improving webcam plugin (@arturi, @goto-bus-stop)
# next
## 0.18.0
To be released: 2017-07-28.
Theme: Dogumentation.
- [ ] test: add https://github.com/pa11y/pa11y for automated accessibility testing (@arturi)
- [ ] webcam: look into simplifying / improving webcam plugin (@arturi, @goto-bus-stop)
- [ ] core: add error in file progress state? (@arturi)
- [ ] core: research !important styles to be immune to any environment/page. Maybe use smth like `postcss-safe-important`. Or increase specificity (with .Uppy) (@arturi)
- [ ] uppy-server: add uppy-server metrics to Librato (@ifedapoolarewaju)
@ -86,14 +93,16 @@ To be released: 2017-07-28.
- [ ] uppy: flag to upload all files, even `uploadComplete` ones (@arturi)
- [x] transloadit: emit an event when an assembly is created (@goto-bus-stop / #244)
- [ ] dashboard: flag to hide the upload button, for cases when you want to manually stat the upload (@arturi)
- [ ] webcam: add 1, 2, 3, smile! to webcam (@arturi #187)
- [x] webcam: add 1, 2, 3, smile! to webcam (@arturi #187)
- [ ] transloadit: function option for file-dependent `params` (@goto-bus-stop)
- [ ] dashboard: flag to hide the upload button, for cases when you want to manually stat the upload (@arturi)
- [ ] docs: on using plugins, all options, list of plugins, i18n (@arturi, @goto-bus-stop, @ifedapoolarewaju)
- [ ] core: calling `upload` immediately after `addFile` does not upload all files (#249 @goto-bus-stop)
- [ ] website: new demo video / gif (@arturi)
- [x] website: live example on the homepage, “try me” (@arturi)
- [ ] DnD Bar (@arturi)
- [x] provider: use informer to display errors (@ifedapoolarewaju)
- [ ] handle error when upload cant start (offline, wrong endpoint) — now it just sits there (@arturi @goto-bus-stop)
- [ ] improve docs (@arturi @goto-bus-stop)
- [ ] GoldenRetriver: recover selected or in progress files after a browser crash or closed tab (@arturi @goto-bus-stop @nqst #268)
## 0.17.0

Binary file not shown.

4863
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,7 @@
"url": "git+https://github.com/transloadit/uppy.git"
},
"author": "",
"license": "ISC",
"license": "MIT",
"bugs": {
"url": "https://github.com/transloadit/uppy/issues"
},

View file

@ -294,6 +294,15 @@ class Uppy {
})
}
/**
* Get a file object.
*
* @param {string} fileID The ID of the file object to return.
*/
getFile (fileID) {
return this.getState().files[fileID]
}
removeFile (fileID) {
const updatedFiles = Object.assign({}, this.getState().files)
delete updatedFiles[fileID]
@ -660,7 +669,7 @@ class Uppy {
// Install all plugins
// this.installAll()
return
return this
}
upload () {
@ -677,7 +686,7 @@ class Uppy {
const waitingFileIDs = []
Object.keys(this.state.files).forEach((fileID) => {
const file = this.state.files[fileID]
const file = this.getFile(fileID)
// TODO: replace files[file].isRemote with some logic
//
// filter files that are now yet being uploaded / havent been uploaded

View file

@ -24,7 +24,7 @@ const MetaData = require('./plugins/MetaData.js')
// Uploaders
const Tus10 = require('./plugins/Tus10')
const Multipart = require('./plugins/Multipart')
const XHRUpload = require('./plugins/XHRUpload')
const Transloadit = require('./plugins/Transloadit')
module.exports = {
@ -39,7 +39,7 @@ module.exports = {
Instagram,
FileInput,
Tus10,
Multipart,
XHRUpload,
Transloadit,
Dashboard,
MetaData,

View file

@ -27,7 +27,9 @@ module.exports = class Client {
data.append('signature', signature)
}
data.append('fields', JSON.stringify(fields))
Object.keys(fields).forEach((key) => {
data.append(key, fields[key])
})
data.append('tus_num_expected_upload_files', expectedFiles)
return fetch(`${this.apiUrl}/assemblies`, {

View file

@ -78,7 +78,7 @@ module.exports = class Transloadit extends Plugin {
const options = this.opts
return Promise.all(
fileIDs.map((fileID) => {
const file = this.getFile(fileID)
const file = this.core.getFile(fileID)
const promise = Promise.resolve(options.getAssemblyOptions(file, options))
return promise.then((assemblyOptions) => {
this.validateParams(assemblyOptions.params)
@ -156,7 +156,7 @@ module.exports = class Transloadit extends Plugin {
this.core.setState({ files })
this.core.emit('transloadit:assembly', assembly, fileIDs)
this.core.emit('transloadit:assembly-created', assembly, fileIDs)
return this.connectSocket(assembly)
}).then(() => {
@ -255,10 +255,6 @@ module.exports = class Transloadit extends Plugin {
})
}
getFile (fileID) {
return this.core.state.files[fileID]
}
prepareUpload (fileIDs) {
fileIDs.forEach((fileID) => {
this.core.emit('core:preprocess-progress', fileID, {
@ -289,7 +285,7 @@ module.exports = class Transloadit extends Plugin {
// If we don't have to wait for encoding metadata or results, we can close
// the socket immediately and finish the upload.
if (!this.shouldWait()) {
const file = this.getFile(fileID)
const file = this.core.getFile(fileID)
const socket = this.socket[file.assembly]
socket.close()
return
@ -304,7 +300,7 @@ module.exports = class Transloadit extends Plugin {
})
const onAssemblyFinished = (assembly) => {
const file = this.getFile(fileID)
const file = this.core.getFile(fileID)
// An assembly for a different upload just finished. We can ignore it.
if (assembly.assembly_id !== file.transloadit.assembly) {
return
@ -324,7 +320,7 @@ module.exports = class Transloadit extends Plugin {
}
const onAssemblyError = (assembly, error) => {
const file = this.getFile(fileID)
const file = this.core.getFile(fileID)
// An assembly for a different upload just errored. We can ignore it.
if (assembly.assembly_id !== file.transloadit.assembly) {
return

View file

@ -131,6 +131,7 @@ module.exports = class Tus10 extends Plugin {
}
optsTus.onProgress = (bytesUploaded, bytesTotal) => {
this.onReceiveUploadUrl(file, upload.url)
this.core.emitter.emit('core:upload-progress', {
uploader: this,
id: file.id,
@ -249,6 +250,26 @@ module.exports = class Tus10 extends Plugin {
})
}
getFile (fileID) {
return this.core.state.files[fileID]
}
onReceiveUploadUrl (file, uploadURL) {
const currentFile = this.getFile(file.id)
// Only do the update if we didn't have an upload URL yet.
if (!currentFile.tus || currentFile.tus.uploadUrl !== uploadURL) {
const newFile = Object.assign({}, currentFile, {
tus: Object.assign({}, currentFile.tus, {
uploadUrl: uploadURL
})
})
const files = Object.assign({}, this.core.state.files, {
[currentFile.id]: newFile
})
this.core.setState({ files })
}
}
onFileRemove (fileID, cb) {
this.core.emitter.on('core:file-remove', (targetFileID) => {
if (fileID === targetFileID) cb()
@ -266,16 +287,14 @@ module.exports = class Tus10 extends Plugin {
onPauseAll (fileID, cb) {
this.core.emitter.on('core:pause-all', () => {
const files = this.core.getState().files
if (!files[fileID]) return
if (!this.core.getFile(fileID)) return
cb()
})
}
onResumeAll (fileID, cb) {
this.core.emitter.on('core:resume-all', () => {
const files = this.core.getState().files
if (!files[fileID]) return
if (!this.core.getFile(fileID)) return
cb()
})
}
@ -300,10 +319,7 @@ module.exports = class Tus10 extends Plugin {
}
this.core.log('Tus is uploading...')
const filesToUpload = fileIDs.map(getFile, this)
function getFile (fileID) {
return this.core.state.files[fileID]
}
const filesToUpload = fileIDs.map((fileID) => this.core.getFile(fileID))
this.uploadFiles(filesToUpload)

View file

@ -2,19 +2,25 @@ const Plugin = require('./Plugin')
const UppySocket = require('../core/UppySocket')
const Utils = require('../core/Utils')
module.exports = class Multipart extends Plugin {
module.exports = class XHRUpload extends Plugin {
constructor (core, opts) {
super(core, opts)
this.type = 'uploader'
this.id = 'Multipart'
this.title = 'Multipart'
this.id = 'XHRUpload'
this.title = 'XHRUpload'
// Default options
const defaultOptions = {
formData: true,
fieldName: 'files[]',
method: 'post',
metaFields: null,
responseUrlFieldName: 'url',
bundle: true,
headers: {}
headers: {},
getResponseData (xhr) {
return JSON.parse(xhr.response)
}
}
// Merge default options with the ones set by user
@ -23,25 +29,38 @@ module.exports = class Multipart extends Plugin {
this.handleUpload = this.handleUpload.bind(this)
}
createFormDataUpload (file, opts) {
const formPost = new FormData()
const metaFields = Array.isArray(opts.metaFields)
? opts.metaFields
// Send along all fields by default.
: Object.keys(file.meta)
metaFields.forEach((item) => {
formPost.append(item, file.meta[item])
})
formPost.append(opts.fieldName, file.data)
return formPost
}
createBareUpload (file, opts) {
return file.data
}
upload (file, current, total) {
const opts = Object.assign({},
this.opts,
this.core.state.xhrUpload || {},
file.xhrUpload || {}
)
this.core.log(`uploading ${current} of ${total}`)
return new Promise((resolve, reject) => {
// turn file into an array so we can use bundle
// if (!this.opts.bundle) {
// files = [files[current]]
// }
// for (let i in files) {
// formPost.append(this.opts.fieldName, files[i])
// }
const formPost = new FormData()
formPost.append(this.opts.fieldName, file.data)
Object.keys(file.meta).forEach((item) => {
// console.log(file.meta, file.meta[item])
formPost.append(item, file.meta[item])
})
const data = opts.formData
? this.createFormDataUpload(file, opts)
: this.createBareUpload(file, opts)
const xhr = new XMLHttpRequest()
@ -58,8 +77,8 @@ module.exports = class Multipart extends Plugin {
xhr.addEventListener('load', (ev) => {
if (ev.target.status >= 200 && ev.target.status < 300) {
const resp = JSON.parse(xhr.response)
const uploadURL = resp[this.opts.responseUrlFieldName]
const resp = opts.getResponseData(xhr)
const uploadURL = resp[opts.responseUrlFieldName]
this.core.emitter.emit('core:upload-success', file.id, resp, uploadURL)
@ -75,7 +94,7 @@ module.exports = class Multipart extends Plugin {
// var upload = {}
//
// if (this.opts.bundle) {
// if (opts.bundle) {
// upload = {files: files}
// } else {
// upload = {file: files[current]}
@ -87,13 +106,13 @@ module.exports = class Multipart extends Plugin {
return reject('Upload error')
})
xhr.open('POST', this.opts.endpoint, true)
xhr.open(opts.method.toUpperCase(), opts.endpoint, true)
Object.keys(this.opts.headers).forEach((header) => {
xhr.setRequestHeader(header, this.opts.headers[header])
Object.keys(opts.headers).forEach((header) => {
xhr.setRequestHeader(header, opts.headers[header])
})
xhr.send(formPost)
xhr.send(data)
this.core.emitter.on('core:upload-cancel', (fileID) => {
if (fileID === file.id) {
@ -112,6 +131,7 @@ module.exports = class Multipart extends Plugin {
}
uploadRemote (file, current, total) {
const opts = Object.assign({}, this.opts, file.xhrUpload || {})
return new Promise((resolve, reject) => {
this.core.emitter.emit('core:upload-started', file.id)
@ -123,9 +143,9 @@ module.exports = class Multipart extends Plugin {
'Content-Type': 'application/json'
},
body: JSON.stringify(Object.assign({}, file.remote.body, {
endpoint: this.opts.endpoint,
endpoint: opts.endpoint,
size: file.data.size,
fieldname: this.opts.fieldName
fieldname: opts.fieldName
}))
})
.then((res) => {
@ -141,7 +161,7 @@ module.exports = class Multipart extends Plugin {
socket.on('progress', (progressData) => Utils.emitSocketProgress(this, progressData, file))
socket.on('success', (data) => {
this.core.emitter.emit('core:upload-success', file.id, data)
this.core.emitter.emit('core:upload-success', file.id, data, data.url)
socket.close()
return resolve()
})
@ -173,11 +193,11 @@ module.exports = class Multipart extends Plugin {
handleUpload (fileIDs) {
if (fileIDs.length === 0) {
this.core.log('Multipart: no files to upload!')
this.core.log('XHRUpload: no files to upload!')
return Promise.resolve()
}
this.core.log('Multipart is uploading...')
this.core.log('XHRUpload is uploading...')
const files = fileIDs.map(getFile, this)
function getFile (fileID) {
return this.core.state.files[fileID]

View file

@ -4,9 +4,9 @@ import Tus10 from 'uppy/lib/plugins/Tus10'
import MetaData from 'uppy/lib/plugins/MetaData'
 
const uppy = Uppy({autoProceed: false})
uppy.use(Dashboard, {trigger: '#select-files', target: '#upload-form', replaceTargetContent: true})
uppy.use(Tus10, {endpoint: '://master.tus.io/files/'})
uppy.use(MetaData, {
.use(Dashboard, {trigger: '#select-files', target: '#upload-form', replaceTargetContent: true})
.use(Tus10, {endpoint: '://master.tus.io/files/'})
uppy.use(MetaData, {
fields: [
{ id: 'resizeTo', name: 'Resize to', value: 1200, placeholder: 'specify future image size' },
{ id: 'description', name: 'Description', value: 'none', placeholder: 'describe what the file is for' }

View file

@ -1,15 +1,15 @@
const Uppy = require('uppy/lib/core/Core')
const FileInput = require('uppy/lib/plugins/FileInput')
const Multipart = require('uppy/lib/plugins/Multipart')
const XHRUpload = require('uppy/lib/plugins/XHRUpload')
const ProgressBar = require('uppy/lib/plugins/ProgressBar')
const uppy = new Uppy({debug: true, autoProceed: true})
uppy
.use(FileInput)
.use(Multipart, {
.use(XHRUpload, {
endpoint: '//api2.transloadit.com',
bundle: true,
formData: true,
fieldName: 'files[]'
})
// by default Uppy removes everything inside target container,
@ -21,4 +21,4 @@ uppy
})
.run()
console.log('Uppy with Formtag and Multipart is loaded')
console.log('Uppy with Formtag and XHRUpload is loaded')

View file

@ -87,15 +87,15 @@
var TUS_ENDPOINT = PROTOCOL + '://master.tus.io/files/'
var uppy = Uppy.Core({debug: true, autoProceed: false})
uppy.use(Uppy.Dashboard, {trigger: '#select-files', target: '#upload-form', replaceTargetContent: true})
uppy.use(Uppy.Tus10, {endpoint: TUS_ENDPOINT})
uppy.use(Uppy.MetaData, {
.use(Uppy.Dashboard, {trigger: '#select-files', target: '#upload-form', replaceTargetContent: true})
.use(Uppy.Tus10, {endpoint: TUS_ENDPOINT})
.use(Uppy.MetaData, {
fields: [
{ id: 'resizeTo', name: 'Resize to', value: 1200, placeholder: 'specify future image size' },
{ id: 'description', name: 'Description', value: 'none', placeholder: 'describe what the file is for' }
]
})
uppy.run()
.run()
uppy.on('core:success', (files) => {
console.log(`Upload complete! Weve uploaded these files: ${files}`)

View file

@ -4,4 +4,4 @@ save it as a layout partial
-->
<figure class="highlight bash"><table><tr><td class="code"><pre><div class="line">$ npm install uppy</div></pre></td></tr></table></figure>
<figure class="highlight js"><figcaption><span>api-usage-example.ejs</span><a href="/examples/../api-usage-example.ejs">view raw</a></figcaption><table><tr><td class="code"><pre><div class="line"><span class="keyword">import</span> Uppy <span class="keyword">from</span> <span class="string">'uppy/lib/core'</span></div><div class="line"><span class="keyword">import</span> Dashboard <span class="keyword">from</span> <span class="string">'uppy/lib/plugins/Dashboard'</span></div><div class="line"><span class="keyword">import</span> Tus10 <span class="keyword">from</span> <span class="string">'uppy/lib/plugins/Tus10'</span></div><div class="line"><span class="keyword">import</span> MetaData <span class="keyword">from</span> <span class="string">'uppy/lib/plugins/MetaData'</span></div><div class="line"> </div><div class="line"><span class="keyword">const</span> uppy = Uppy({autoProceed: <span class="literal">false</span>})</div><div class="line">uppy.use(Dashboard, {trigger: <span class="string">'#select-files'</span>, target: <span class="string">'#upload-form'</span>, replaceTargetContent: <span class="literal">true</span>})</div><div class="line">uppy.use(Tus10, {endpoint: <span class="string">'://master.tus.io/files/'</span>})</div><div class="line">uppy.use(MetaData, {</div><div class="line"> fields: [</div><div class="line"> { id: <span class="string">'resizeTo'</span>, name: <span class="string">'Resize to'</span>, value: <span class="number">1200</span>, placeholder: <span class="string">'specify future image size'</span> },</div><div class="line"> { id: <span class="string">'description'</span>, name: <span class="string">'Description'</span>, value: <span class="string">'none'</span>, placeholder: <span class="string">'describe what the file is for'</span> }</div><div class="line"> ]</div><div class="line"> })</div><div class="line">uppy.run()</div><div class="line"> </div><div class="line">uppy.on(<span class="string">'core:success'</span>, (files) =&gt; {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`Upload complete! Weve uploaded these files: <span class="subst">${files}</span>`</span>)</div><div class="line">})</div></pre></td></tr></table></figure>
<figure class="highlight js"><figcaption><span>api-usage-example.ejs</span><a href="/examples/../api-usage-example.ejs">view raw</a></figcaption><table><tr><td class="code"><pre><div class="line"><span class="keyword">import</span> Uppy <span class="keyword">from</span> <span class="string">'uppy/lib/core'</span></div><div class="line"><span class="keyword">import</span> Dashboard <span class="keyword">from</span> <span class="string">'uppy/lib/plugins/Dashboard'</span></div><div class="line"><span class="keyword">import</span> Tus10 <span class="keyword">from</span> <span class="string">'uppy/lib/plugins/Tus10'</span></div><div class="line"><span class="keyword">import</span> MetaData <span class="keyword">from</span> <span class="string">'uppy/lib/plugins/MetaData'</span></div><div class="line"> </div><div class="line"><span class="keyword">const</span> uppy = Uppy({autoProceed: <span class="literal">false</span>})</div><div class="line"> .use(Dashboard, {trigger: <span class="string">'#select-files'</span>, target: <span class="string">'#upload-form'</span>, replaceTargetContent: <span class="literal">true</span>})</div><div class="line"> .use(Tus10, {endpoint: <span class="string">'://master.tus.io/files/'</span>})</div><div class="line"> uppy.use(MetaData, {</div><div class="line"> fields: [</div><div class="line"> { id: <span class="string">'resizeTo'</span>, name: <span class="string">'Resize to'</span>, value: <span class="number">1200</span>, placeholder: <span class="string">'specify future image size'</span> },</div><div class="line"> { id: <span class="string">'description'</span>, name: <span class="string">'Description'</span>, value: <span class="string">'none'</span>, placeholder: <span class="string">'describe what the file is for'</span> }</div><div class="line"> ]</div><div class="line"> })</div><div class="line">uppy.run()</div><div class="line"> </div><div class="line">uppy.on(<span class="string">'core:success'</span>, (files) =&gt; {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">`Upload complete! Weve uploaded these files: <span class="subst">${files}</span>`</span>)</div><div class="line">})</div></pre></td></tr></table></figure>