使用 DropzoneJS 获取文件内容

Getting file contents when using DropzoneJS

我真的很喜欢 DropZoneJS 组件,目前正在将它包装在 EmberJS 组件中(您可以看到 demo here)。无论如何,包装器工作得很好,但我想监听 Dropzone 的事件之一并检查文件内容(而不是像大小、lastModified 等元信息)。我正在处理的文件类型是 XML 文件,我想在发送前查看 "into" 以验证它。

如何才能做到这一点?我原以为内容会挂在 file 对象之外,您可以在许多事件中获取该对象,但除非我只是遗漏了一些明显的东西,否则它不存在。 :(

好的,我已经回答了我自己的问题,既然其他人似乎感兴趣,我将 post 在这里回答。您可以在这里找到它的工作演示:

https://ui-dropzone.firebaseapp.com/demo-local-data

在演示中,我将 Dropzone 组件包装在 EmberJS 框架中,但如果您查看代码,您会发现它只是 Javascript 代码,没什么好害怕的。 :)

我们要做的事情是:

  • 获取网络请求前的文件

    我们需要熟悉的关键是HTML5 API。好消息是它非常简单。看看这段代码,也许这就是您所需要的:

    /**
     * Replaces the XHR's send operation so that the stream can be
     * retrieved on the client side instead being sent to the server.
     * The function name is a little confusing (other than it replaces the "send"
     * from Dropzonejs) because really what it's doing is reading the file and
     * NOT sending to the server.
     */
    _sendIntercept(file, options={}) {
      return new RSVP.Promise((resolve,reject) => {
        if(!options.readType) {
          const mime = file.type;
          const textType = a(_textTypes).any(type => {
            const re = new RegExp(type);
            return re.test(mime);
          });
          options.readType = textType ? 'readAsText' : 'readAsDataURL';
        }
        let reader = new window.FileReader();
        reader.onload = () => {
          resolve(reader.result);
        };
        reader.onerror = () => {
          reject(reader.result);
        };
    
        // run the reader
        reader[options.readType](file);
      });
    },
    

    https://github.com/lifegadget/ui-dropzone/blob/0.7.2/addon/mixins/xhr-intercept.js#L10-L38

    上面的代码 returns 是一个 Promise,一旦放入浏览器的文件 "read" 进入 Javascript,它就会解析。这应该非常快,因为它都是本地的(请注意,如果您正在下载非常大的文件,您可能想要 "chunk" 它......这是一个更高级的主题)。

  • 挂接到 Dropzone

    现在我们需要在 Dropzone 中找到挂钩的地方以读取文件内容并停止我们不再需要的网络请求。由于 HTML5 文件 API 只需要一个 File 对象,您会注意到 Dropzone 为此提供了各种挂钩。

    我决定使用 "accept" 挂钩,因为它会让我有机会下载文件并一次性验证所有内容(对我来说,这主要是关于拖放 XML 等等该文件的内容是验证过程的 一部分 ),并且重要的是它发生在 之前 网络请求。

    现在重要的是您要意识到我们是 "replacing" accept 函数,而不是 侦听 它触发的事件。如果我们只是听,我们仍然会招致网络请求。所以为了**超载*接受我们做这样的事情:

    this.accept = this.localAcceptHandler; // replace "accept" on Dropzone
    

    这仅在 thisDropzone 对象时有效。您可以通过以下方式实现:

    • 将其包含在您的 init 挂钩函数中
    • 将其作为实例化的一部分(例如,new Dropzone({accept: {...}

    现在我们提到了"localAcceptHandler",让我向您介绍一下:

    localAcceptHandler(file, done) {
      this._sendIntercept(file).then(result => {
        file.contents = result;
        if(typeOf(this.localSuccess) === 'function') {
          this.localSuccess(file, done);
        } else {
          done(); // empty done signals success
        }
      }).catch(result => {
        if(typeOf(this.localFailure) === 'function') {
          file.contents = result;
          this.localFailure(file, done);
        } else {
          done(`Failed to download file ${file.name}`);
          console.warn(file);
        }
      });
    }
    

    https://github.com/lifegadget/ui-dropzone/blob/0.7.2/addon/mixins/xhr-intercept.js#L40-L64

    简而言之,它执行以下操作:

    • 读取文件内容(又名,_sendIntercept
    • 基于 mime 类型通过 readAsTextreadAsDataURL
    • 读取文件
    • 将文件内容保存到.contents属性文件
  • 停止发送

    为了拦截网络上请求的发送但仍然保持工作流的其余部分,我们将替换一个名为 submitRequest 的函数。在 Dropzone 代码中,这个函数是一个单行代码,我所做的是用我自己的单行代码替换它:

    this._finished(文件,'locally resolved, refer to "contents" property');

    https://github.com/lifegadget/ui-dropzone/blob/0.7.2/addon/mixins/xhr-intercept.js#L66-L70

  • 提供 访问权限 检索文档

    最后一步只是为了确保我们的 localAcceptHandler 代替 dropzone 提供的 accept 例程 :

    https://github.com/lifegadget/ui-dropzone/blob/0.7.2/addon/components/drop-zone.js#L88-L95

这对我有用:

Dropzone.options.PDFDrop = {
    maxFilesize: 10, // Mb
    accept: function(file, done) {
        var reader = new FileReader();
        reader.addEventListener("loadend", function(event) { console.log(event.target.result);});
        reader.readAsText(file);
    }
};

如果是二进制数据也可以使用reader.reaAsBinaryString()

使用 FileReader() 解决方案对我来说效果惊人:

Dropzone.autoDiscover = false;
var dz = new Dropzone("#demo-upload",{
   autoProcessQueue:false,
   url:'upload.php'
});

dz.on("drop",function drop(e) {
              var files = [];
              for (var i = 0; i < e.dataTransfer.files.length; i++) {
                files[i] = e.dataTransfer.files[i];
              }


var reader = new FileReader();
reader.onload = function(event) {
    var line = event.target.result.split('\n');
    for ( var i = 0; i < line.length; i++){
        console.log(line);
    }
};
reader.readAsText(files[files.length-1]);