使用 DropzoneJS 获取文件内容
Getting file contents when using DropzoneJS
我真的很喜欢 DropZoneJS 组件,目前正在将它包装在 EmberJS 组件中(您可以看到 demo here)。无论如何,包装器工作得很好,但我想监听 Dropzone 的事件之一并检查文件内容(而不是像大小、lastModified 等元信息)。我正在处理的文件类型是 XML 文件,我想在发送前查看 "into" 以验证它。
如何才能做到这一点?我原以为内容会挂在 file
对象之外,您可以在许多事件中获取该对象,但除非我只是遗漏了一些明显的东西,否则它不存在。 :(
好的,我已经回答了我自己的问题,既然其他人似乎感兴趣,我将 post 在这里回答。您可以在这里找到它的工作演示:
在演示中,我将 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
这仅在 this
是 Dropzone 对象时有效。您可以通过以下方式实现:
- 将其包含在您的
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 类型通过
readAsText
或 readAsDataURL
读取文件
- 将文件内容保存到
.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]);
我真的很喜欢 DropZoneJS 组件,目前正在将它包装在 EmberJS 组件中(您可以看到 demo here)。无论如何,包装器工作得很好,但我想监听 Dropzone 的事件之一并检查文件内容(而不是像大小、lastModified 等元信息)。我正在处理的文件类型是 XML 文件,我想在发送前查看 "into" 以验证它。
如何才能做到这一点?我原以为内容会挂在 file
对象之外,您可以在许多事件中获取该对象,但除非我只是遗漏了一些明显的东西,否则它不存在。 :(
好的,我已经回答了我自己的问题,既然其他人似乎感兴趣,我将 post 在这里回答。您可以在这里找到它的工作演示:
在演示中,我将 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
这仅在
this
是 Dropzone 对象时有效。您可以通过以下方式实现:- 将其包含在您的
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 类型通过
readAsText
或readAsDataURL
读取文件
- 将文件内容保存到
.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]);