当前位置: 首页 > 后端技术 > Node.js

前端实现文件下载和拖拽上传

时间:2023-04-03 23:00:41 Node.js

在需求页添加下载示例按钮,实现word文件可以拖拽上传的区域,限制文件大小和文件类型到2MB,可以显示进度条,同时可以取消上传。文件下载业务中需要的是样本放在静态文件夹中,不需要请求后台。针对这个场景,笔者将介绍三种方法,分别是window.open、form表单提交和tag下载。笔者将通过下载img和word文档的例子来比较这三种方式。现在构建dom结构如下:window.openformSubmitaDownload方法一:使用window.open:importgakkiURLfrom'./gakki.jpg';importwordURLfrom'./wordURL.doc';windowOpen=()=>{window.open(gakkiURL);//window.open(wordURL);}该方法请求两个文件时,具体表现为:img:打开一个新的网页,然后显示对应的img图片。word:下载文件。方法二:使用表单提交form:formSubmit=()=>{letform=document.createElement('form');form.method='get';form.action=gakkiURL;//form.action=wordURL;//form.target='_blank';//形成新打开的页面document.body.appendChild(form);//form表单提交操作首先要添加到dom树中form.submit();文档.正文。removeChild(form);}该方法请求两个文件时,具体表现为:img:form未设置target时会在当前页面打开url并显示图片。word:下载文件。从以上两种方式可以看出,在请求相应的url时,浏览器会针对不同的MIME类型选择不同的处理方式。当请求img、txt等格式时,浏览器会打开对应的文件,而不是下载。如果要下载这些格式的img怎么办?这时候就需要方法三了。方法3:使用标签://使用标签aDownload=()=>{consta=document.createElement('a');a.href=gakkiURL;//a.href=wordURL;//a.download='gakki.jpg';a.click();}a标签在不添加download属性的情况下和上面两个方法的行为一样,但是添加download属性后,可以成功触发img等格式的下载。download:该属性可以设置一个值来指定下载文件的名称。允许的值没有限制,浏览器会自动检测正确的文件扩展名并将其添加到文件中(.img、.pdf、.txt、.html等)。最后对比效果:文件拖拽上传文件上传常用的方法是使用type="file"的input标签触发下载,然后使用formData传输数据。代码如下://点击上传文档handleClick=()=>{constinput=document.createElement('input');input.type='文件';input.accept='application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document';//word文件对应的MIME类型input.onchange=(e)=>{constfile=e.target.files.items(0);//files[0]也可以console.table(file);//检查文档格式if(!this.checkDocument()){e.target.value='';返回;}//上传文档this.uploadDocument(file);};input.click();};accept:表示输入接受的文件的MIME类型。.doc和.docx的相应MIME类型在源代码中指示。fileList对象可以以items或数组索引的形式获取对应的文件对象。文件对象常用的属性有:lastModified、type、name、size。可以通过这些属性自定义检验文档格式。校验文档checkDocument的代码如下://校验文档checkDocument=file=>{constaccept=['.doc','.docx'];constindex=file.name.lastIndexOf('.');if(index<0||accept.indexOf(file.name.substr(index).toLowerCase())<0){//检查文件类型Message.error('暂不支持此文件格式');返回假;}if(file.size>2*1024*1024){//检查文件大小Message.error('文件大于2MB,上传失败');返回假;}返回真;};之后,上传文档uploadDocument://uploadDocument=file=>{constindex=file.name.lastIndexOf('.');constfileName=file.name.slice(0,index);constformData=newFormData();formData.append('文件',文件);//通过ajax、fetch或axios上传...};上传文件后,需要获取上传进度显示进度条。下面分别介绍ajax、fetch、axios对进度事件的支持。Ajax原生支持progress事件,可以用来获取上传进度和下载进度,分别是xhr.upload.onprogress和xhr.onprogress事件。代码如下:xhr.upload.onprogress=ev=>console.log((ev.loaded/ev.total)*100)另外可以使用xhr.abort()取消文件上传。fetch不支持progress事件,所以无法获取上传进度。但是查资料的时候发现,由于res.body是一个可读的字节流对象,所以可以使用res.body对象支持的getReader()属性来获取下载进度。具体文档请参考jakearchibald.com/2016/stream...。此处代码与上传需求无关,仅作为fetch的相关扩展,本节可直接跳过。res.body.getReader()方法用于读取响应的原始字节流,可以循环读取,直到body内容传输完成;fetch(url,options).then(res=>{letreader=res.body.getReader();letloaded=0;//read()方法返回一个promise,它在值被接受时解析。reader.read().then(functionprocessResult(result){//结果对象有两个属性://done:完成时为真//值数据if(result.done){//完成时退出循环console.log("Fetchcomplete");return;}loaded+=result.value.length;//长度,单位:byteconsole.log('Received',loaded,'bytesofdatasofar');//循环读取returnreader.read().then(processResult);});});axios通过onUploadProgress和onDownloadProgress实现上传和下载。onUploadProgress(ev)=>{length=Math.round((ev.loaded/ev.total)*100);控制台日志(长度);axios使用取消令牌取消请求varCancelToken=axios.CancelToken;varsource=CancelToken.source();axios.get(url,{cancelToken:source.token})source.cancel();//取消请求总结ajax和axios都很好地支持progress事件,fetch缺乏支持对于这里不提供对进度事件的支持。拖拽事件实现点击上传、获取上传进度、取消上传等功能后,接下来就是实现拖拽上传。在实施之前,我将介绍相关事件。首先是拖动对象时发生的事件:onDragStart、onDrag和onDragEnd,这些事件与被拖动的对象有关。onDragStart:拖动开始onDrag:拖动过程中持续触发onDragEnd:拖动结束,不管能否放置都会触发事件,然后是放置文件时要触发的事件:onDragEnter、onDragOver、onDragLeave和onDrop,事件与要拖放的区域相关。onDragEnter:被拖动对象进入时触发onDragOver:被拖动对象在区域中被拖动时持续触发onDragLeave:被拖动对象离开区域时触发onDrop:被拖动对象被放置在区域中时触发为了能够拖动拖动word文档需要在容器上取消容器默认的onDragEnter和onDragOver事件。这是因为:事件监听器dragenter或dragover事件用于指示一个有效的放置目标,即拖放项可能被放置的地方。网页或应用程序的大部分区域都不是放置数据的有效位置。因此,这些事件的默认处理是不允许丢弃。如果要允许删除,则必须通过取消事件来阻止默认处理。您可以通过从属性定义的事件侦听器返回false或调用事件的preventDefault方法来执行此操作。后者在单独脚本中定义的函数中可能更可行。dom结构如下:renderBtnByUpload(this.state.uploadStatus)}//根据上传状态判断是“上传文件”还是“取消上传”

将文件拖到内容区放置后,即可获取文件信息通过数据传输对象。最终的handleDrop事件如下://拖放上传handleDrop=(e)=>{constfile=e.dataTransfer.files[0];if(e.dataTransfer.files.length>1){Message.error('只支持上传word文件');返回;}if(!this.checkDocument(file)){//如果上传失败,直接退出e.target.value='';返回;}this.uploadDocument(文件);//上传文件}总结最后,实现的大致思路是先构建一个放置文件的容器,然后取消容器默认的onDragOver和onDragEnter事件。将文件拖入容器时,通过dataTransfer.files获取文件并上传,使用ajax或axios获取progress事件的长度,将长度传给progressBar组件,最后显示出来。