这几天做一个项目的迭代开发,需要在react 中使用plupload 插件实现上传文件。需求很简单,如下图,点击“...” 按钮选择文件,点击“Import”按钮上传文件。
plupload 上传文件大概分为以下几步:
1. 新建一个uploader实例,并在构造时配置好上传的相关属性。
const uploader = new plupload.Uploader({
browse_button: 'selectFileId', //指定调起选择文件对话框的DOM 元素或其ID,注意是DOM元素
url: ‘put upload file url here’,
multi_selection: false,
init: { //在里面定义各种事件的回调
BeforeUpload: (uploader,file) => {
},
FilesAdded:(uploader,files)=>{
},
FileUploaded:(uploader,file,info) =>{
},
}
})
2. 初始化实例
this.uploader.init();
3. 在确定导入的按钮相关触发事件(例如onclick)的回调中调用实例的start方法,开始上传
handleImport = () => {
this.uploader.start();
}
但是在调试过程中问题一就出现了。
问题一:发现点击“...” 按钮无法调起选择文件对话框。跟踪发现init()函数也有被调用,说明uploader初始化没成功。反复检查配置,没发现配置有任何问题。既然配置没问题,那就可能是初始化的时机不对。检查发现,uploader的实例创建和init初始化都放在了 react component的 componentWillMount() 函数中。 突然醒悟,componentWillMount 是在react 创建的组件插入DOM节点之前触发的,也就是说调用时DOM中还没有ID为selectFlagId的元素,uploader 初始化时当然是找不到触发DOM的。谜底解开。。。
问题一解决后,接着测试,又发现了问题二。
问题二:当第一次触发上传,服务端检查发现文件内容不符合规范,返回错误。这个时候,用户直接打开文件修改保存,再在界面直接点击上传按钮,发现无法上传文件,但是重新选择文件后,又可以上传。打开chrome 开发工具,检查网络请求,发现无上传请求发出。然后设断点跟踪,发现handleImport() 函数是调用了的,uploader 的start 函数也是调用了的,但是BeforeUpload函数没有进入。也就是说是uploader 在接受到start信号后,没有触发上传。此刻小编脑上各种⭕️, 到这里,就需要去了解pluploader内部的上传机制了。打开plupload 官方文档查阅,发现在plupload内部有一个上传队列,每次选择文件后,都会把文件放入队列中,每次start时,都会从队列中拿任务。但是由于第一上传已经触发,任务已经执行;第二次点击时,任务队列其实是空的,拿不到任务,自然不会上传;如果再次选择文件,文件又会被放入队列,因此可以拿到任务,进行上传。谜底揭晓了。
那问题来了,怎么解决呢?
首次需求是要满足的,不能要求用户一定要再选一次文件吧,毕竟用户是我们的衣食父母啊;
其次,怎么样通过接口往队列里加任务,选择文件时调用的接口是不是public接口;
最后,文件对象还有吗,怎么获取。
继续搜索官方文档API,发现pluploader有一个addFile(file, [fileName])的接口, 官方解释如下:
“Adds file to the queue programmatically. Can be native file, instance of Plupload.File, instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded, if any files were added to the queue. Otherwise nothing happens.” https://www.plupload.com/docs/v2/Uploader#addFile-method-filefileName
大概翻译下,该接口用于程序内部调用增加文件到队列中。文件可以是本地文件,Plupload.File的实例,mOxie.File 实例,input[type="file"] 元素,或者是这些内容的数组。如果有文件增加到队列中,FilesAdded 事件会被触发,反之,则不会触发事件。
好了,接口有了,文件对象呢? 根据接口描述,接口接受的的文件对象可选择多种。但是哪一种是目前可以获取的呢?首先想到的是Plupload.File的实例,查询对象uploader对象的属性,发现有一个files属性,官方描述为:“Current upload queue, an array of File instances.”
/**
* Current upload queue, an array of File instances.
*
* @property files
* @type Array
* @see plupload.File
*/
files : files,
第一次看到这个描述时,小编认为文件一旦上传完,就会从队列里移除,那这个files应该是个空数组啊。但是debug 发现,并非如此。这个files属性里会一直保存增加的File instances,只是用status来标记File的状态。官方描述:“Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE.”
欣喜若狂啊,直接取最后一个File instance,作为addFile 的参数不就可以了。但是,测试结果是No! 追其原因,可能这个File instances 里面的status导致的,重置status,依然无效,但是意外发现,Plupload.File的实例下面有一个getNative()的方法,官方解释:“Returns native window.File object, when it's available.” 这不就是我们需要的native file么,