Просмотр исходного кода

thumbnail-generator: add waitForThumbnailsBeforeUpload option, false by default (#1803)

* add waitForThumbnailsBeforeUpload option, false by default: enter preprocess stage until all thumbnails are generated and all exif data is added

* check if the queue is empty already, then resolve immediately

* add EXIF data to file meta, use uppy.once instead of on

* add generatingThumbnails locale

* delete the thumbnail from exif metadata, because it contains a blob
Artur Paikin 5 лет назад
Родитель
Сommit
73907ae3c8

+ 3 - 1
packages/@uppy/dashboard/src/index.js

@@ -101,6 +101,7 @@ module.exports = class Dashboard extends Plugin {
       width: 750,
       height: 550,
       thumbnailWidth: 280,
+      waitForThumbnailsBeforeUpload: false,
       defaultPickerIcon,
       showLinkToFileUploadResult: true,
       showProgressDetails: false,
@@ -910,7 +911,8 @@ module.exports = class Dashboard extends Plugin {
     if (!this.opts.disableThumbnailGenerator) {
       this.uppy.use(ThumbnailGenerator, {
         id: `${this.id}:ThumbnailGenerator`,
-        thumbnailWidth: this.opts.thumbnailWidth
+        thumbnailWidth: this.opts.thumbnailWidth,
+        waitForThumbnailsBeforeUpload: this.opts.waitForThumbnailsBeforeUpload
       })
     }
 

+ 1 - 0
packages/@uppy/locales/src/en_US.js

@@ -54,6 +54,7 @@ en_US.strings = {
     '1': 'Added %{smart_count} files from %{folder}',
     '2': 'Added %{smart_count} files from %{folder}'
   },
+  generatingThumbnails: 'Generating thumbnails...',
   import: 'Import',
   importFrom: 'Import from %{name}',
   link: 'Link',

+ 50 - 9
packages/@uppy/thumbnail-generator/src/index.js

@@ -1,5 +1,6 @@
 const Exif = require('exif-js')
 const { Plugin } = require('@uppy/core')
+const Translator = require('@uppy/utils/lib/Translator')
 const dataURItoBlob = require('@uppy/utils/lib/dataURItoBlob')
 const isObjectURL = require('@uppy/utils/lib/isObjectURL')
 const isPreviewSupported = require('@uppy/utils/lib/isPreviewSupported')
@@ -14,16 +15,23 @@ module.exports = class ThumbnailGenerator extends Plugin {
 
   constructor (uppy, opts) {
     super(uppy, opts)
-    this.type = 'thumbnail'
+    this.type = 'modifier'
     this.id = this.opts.id || 'ThumbnailGenerator'
     this.title = 'Thumbnail Generator'
     this.queue = []
     this.queueProcessing = false
     this.defaultThumbnailDimension = 200
 
+    this.defaultLocale = {
+      strings: {
+        generatingThumbnails: 'Generating thumbnails...'
+      }
+    }
+
     const defaultOptions = {
       thumbnailWidth: null,
-      thumbnailHeight: null
+      thumbnailHeight: null,
+      waitForThumbnailsBeforeUpload: false
     }
 
     this.opts = {
@@ -31,9 +39,8 @@ module.exports = class ThumbnailGenerator extends Plugin {
       ...opts
     }
 
-    this.onFileAdded = this.onFileAdded.bind(this)
-    this.onFileRemoved = this.onFileRemoved.bind(this)
-    this.onRestored = this.onRestored.bind(this)
+    this.translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale])
+    this.i18n = this.translator.translate.bind(this.translator)
   }
 
   /**
@@ -107,7 +114,13 @@ module.exports = class ThumbnailGenerator extends Plugin {
 
   getOrientation (file) {
     return new Promise((resolve) => {
-      Exif.getData(file.data, function callback () {
+      const uppy = this.uppy
+      Exif.getData(file.data, function exifGetDataCallback () {
+        const exifdata = Exif.getAllTags(this)
+        // delete the thumbnail from exif metadata, because it contains a blob
+        // and we don’t blobs in meta — it might lead to unexpected issues on the server
+        delete exifdata.thumbnail
+        uppy.setFileMeta(file.id, { exifdata })
         const orientation = Exif.getTag(this, 'Orientation') || 1
         resolve(ORIENTATIONS[orientation])
       })
@@ -284,13 +297,13 @@ module.exports = class ThumbnailGenerator extends Plugin {
     return Promise.resolve()
   }
 
-  onFileAdded (file) {
+  onFileAdded = (file) => {
     if (!file.preview) {
       this.addToQueue(file)
     }
   }
 
-  onFileRemoved (file) {
+  onFileRemoved = (file) => {
     const index = this.queue.indexOf(file)
     if (index !== -1) {
       this.queue.splice(index, 1)
@@ -302,7 +315,7 @@ module.exports = class ThumbnailGenerator extends Plugin {
     }
   }
 
-  onRestored () {
+  onRestored = () => {
     const { files } = this.uppy.getState()
     const fileIDs = Object.keys(files)
     fileIDs.forEach((fileID) => {
@@ -315,15 +328,43 @@ module.exports = class ThumbnailGenerator extends Plugin {
     })
   }
 
+  waitUntilAllProcessed = (fileIDs) => {
+    fileIDs.forEach((fileID) => {
+      const file = this.uppy.getFile(fileID)
+      this.uppy.emit('preprocess-progress', file, {
+        mode: 'indeterminate',
+        message: this.i18n('generatingThumbnails')
+      })
+    })
+
+    return new Promise((resolve, reject) => {
+      if (this.queueProcessing) {
+        this.uppy.once('thumbnail:all-generated', () => {
+          resolve()
+        })
+      } else {
+        resolve()
+      }
+    })
+  }
+
   install () {
     this.uppy.on('file-added', this.onFileAdded)
     this.uppy.on('file-removed', this.onFileRemoved)
     this.uppy.on('restored', this.onRestored)
+
+    if (this.opts.waitForThumbnailsBeforeUpload) {
+      this.uppy.addPreProcessor(this.waitUntilAllProcessed)
+    }
   }
 
   uninstall () {
     this.uppy.off('file-added', this.onFileAdded)
     this.uppy.off('file-removed', this.onFileRemoved)
     this.uppy.off('restored', this.onRestored)
+
+    if (this.opts.waitForThumbnailsBeforeUpload) {
+      this.uppy.removePreProcessor(this.waitUntilAllProcessed)
+    }
   }
 }

+ 6 - 0
website/src/docs/dashboard.md

@@ -134,6 +134,12 @@ Width of the Dashboard in pixels. Used when `inline: true`.
 
 Height of the Dashboard in pixels. Used when `inline: true`.
 
+### `waitForThumbnailsBeforeUpload: false`
+
+Whether to wait for all thumbnails from `@uppy/thumbnail-generator` to be ready before starting the upload. If set to `true`, Thumbnail Generator will envoke Uppy’s internal processing stage, displaying “Generating thumbnails...” message, and wait for `thumbnail:all-generated` event, before proceeding to the uploading stage.
+
+This is useful because Thumbnail Generator also adds EXIF data to images, and if we wait until it’s done processing, this data will be avilable on the server after the upload.
+
 ### `showLinkToFileUploadResult: true`
 
 By default, when a file upload has completed, the file icon in the Dashboard turns into a link to the uploaded file. If your app does not publicly store uploaded files or if it's otherwise unwanted, pass `showLinkToFileUploadResult: false`.

+ 10 - 2
website/src/docs/thumbnail-generator.md

@@ -17,7 +17,8 @@ const ThumbnailGenerator = require('@uppy/thumbnail-generator')
 
 uppy.use(ThumbnailGenerator, {
   thumbnailWidth: 200,
-  // thumbnailHeight: 200 // optional, use either width or height
+  // thumbnailHeight: 200 // optional, use either width or height,
+  waitForThumbnailsBeforeUpload: false
 })
 ```
 
@@ -47,7 +48,8 @@ The `@uppy/thumbnail-generator` plugin has the following configurable options:
 uppy.use(ThumbnailGenerator, {
   id: 'ThumbnailGenerator',
   thumbnailWidth: 200,
-  thumbnailHeight: 200
+  thumbnailHeight: 200,
+  waitForThumbnailsBeforeUpload: false
 })
 ```
 
@@ -75,6 +77,12 @@ If both width and height are given, only width is taken into account.
 >
 > See https://github.com/transloadit/uppy/issues/979 and https://github.com/transloadit/uppy/pull/1096 for details on this feature.
 
+### `waitForThumbnailsBeforeUpload: false`
+
+Whether to wait for all thumbnails to be ready before starting the upload. If set to `true`, Thumbnail Generator will envoke Uppy’s internal processing stage and wait for `thumbnail:all-generated` event, before proceeding to the uploading stage.
+
+This is useful because Thumbnail Generator also adds EXIF data to images, and if we wait until it’s done processing, this data will be avilable on the server after the upload.
+
 ## Event
 
 `thumbnail:generated` event is emitted with `file` and `preview` local url as arguments: