前端文件相关知识+切片上传+断点续传+word/excel上传及预览知识

38 阅读6分钟

文件操作与文件上传

一、前端把一个文件上传到后端的方案

  1. 二进制blob传输 【formData传输】 【相当于用formData,搭载了blob传输给了后端】
  2. base64传输 【转为base64传输,后端解码后,再转回base64】【使用了fileReader】

二、相关对象

  1. files 通过input标签读过来的文件对象
  2. blob 不可变的二进制内容,包含很多操作方法
  3. formData 用于和后端传输的对象
  4. fileReader 多用与把文件读取为某种形式,如base64,text文本

三、文件对象之间的关系

说明1// files是blob的子类,blob有的方法,files都有。
// 所以,整个文件的切片上传和断点续传都是基于blob的

说明2// 注:当通过input标签选择的文件,拿到了文件对象,但是这个对象不能直接给后端
// 因为files类是前端的对象,后端不认识,所以需要前后端都认可的载体来传递
// 如formData,可以理解为,formData是专门搭载files文件的,这样就可以把文件以二进制的方式传给后端

说明3// 可以通过input上传文件后,获取到file对象
// 也可以通过new File(['sss'],'aa.txt')的方式来创建file对象,但实际不会这样创建file

//说明4:file对象与blob对象相互转换, file对象是指input上传文件后的对象【如 e.target.files[0]】
//  let blob1 = new Blob([file]) 
// new File([blob1],'test.png[这个参数是文件名]')

// let sliceBlob = new Blob([file]).slice(0,5000) ; 【切割】
// new File([sliceBlob],'vvv.txt')

// 说明5 : 将file或者blob对象,转换成base64格式或者文字格式
// let fr = new FileReader()
// fr.readAsDataURL(sliceBlob);
// 因为是异步,所以要监听
fr.onload = function(){
    console.log(fr.result) // 这里才是真正的结果 , base64可以用于做缩略图
}

// 开始传递后端
async submit(){
    let _formData = new FormData()
    _formData.append('file',file对象)
}
axios.post('接口',_formData)

// 总结:
// 如何做缩略图,文本预览
// 接口传输只能通过base64、text、formData格式来传

四、多文件上传

// input文件多选属性,结合push,其实就是循环的多文件上传

五、切片上传

//声明变量
let fileObj  // 文件对象
let percent = 0  // 可以用来记录进度
// input中的change事件
fileChange(e){
   fileObj = e.target.files[0]
}

// 提交按钮事件
async submit(){
    let size = 2*1024*1024   // 切片的每片大小, 比如2m
    let current = 0          // 切片开始的位置
    let fileSize = fileObj.size
    // localStorage.setItem(fileObj,current) // 断点续传
    while(current < fileSize){
      let _formData = new FormData()
      _formData.append(_formData.name,fileObj.slice(current,current+size))
      await axios.post('/api',_formData)
      percent = Math.min((current/fileSize)*100,100)
      current+=size
    }
    file.slice()
}

六、断点续传

利用localStorage,结合切片上传实现
localStorage.setItem(fileObj,current)
//记录current的位置,
// 比如当传到4mb的时候断了,下次再从current为4mb的时候开始

文件下载【前端接受后端传输文件】

一、从后端获取到的二进制文件下载下来有三种方案

  1. 直接打开下载
    • 无法命名,只适用于get直接返回blob的接口
  2. 利用a标签的download
    • 比较合适的方案
  3. file-saver
    • 现成的库,简单方便

方案一:直接打开下载

window.open('http://api.xxx')  // 直接就下载了,浏览器直接打开,无法携带token验证

方案二:利用a标签的download,比较合适的方法

一、 重要的概念:
  1. createObjectURL:把blob对象的内容地址,以url形式给出
  2. msSaveBlob:IE不支持a标签下载,用的是msSaveBlob
  3. a标签的download属性,表面该a标签的i行为是下载,并说明文件名
二、流程总结
//方案一:
1. 按照blob请求接口(必须按blob接受二进制文件)
2. 判断有没有msSaveBlob
3. 若有则用msSaveBlob下载,若没有则用以下流程

//方案二:
//没有msSaveBlob的流程
1. createObjectURL创建文件本地url
2. 创建a标签
3. 创建url给到a标签的href
4. 设置download
5. 模拟点击a标签
6. 下载完成
三、具体实现代码
axios.get('api',{responseType:'blob'}).then(res=>{
    console.log(res.data)
    if(window.navigator.msSaveBlob){
        // 兼容ie , 虽然已经废除
        window.navigator.msSaveBlob(res.data,{type:'blob类型,具体看请求接口的type救好'},'文件名.ppt')
    }else{
      // 方案二
      //1. 创建blob的本地url
      let blobURL = URLcreateObjectURL(res.data)
      //2. 创建a标签
      let link = documentcreateElement('a')
      //3. 设置a标签的链接href
      link.href = blobURL
      //4. 设置doenload,及文件名
      a.download = 'text.ppt'
      //5. 设置隐藏,只是模拟a标签下载,不需要显示
      link.style.display = 'none'
      //6. 模拟点击
      link.click()
      //7. 销毁地址,以免占内存
      URL.revokeObjectURL(blobURL)
    }
})
//这个很常用
四、使用第三方插件库,file-saver
import {saveAs} from 'file-saver'

axios.get('api',{responseType:'blob'}).then(res=>{
    saveAs(res.data,'文件名.ppt')
})

//这个就很简单了
扩展:比如收到后端的文件流之后,把其展示出来,而不是下载 (即,直接预览)
  1. 流程梳理
预览,就是需要一个地址
无论是,图片、excel、word等,总而言之就是给一个地址

url除了线上地址,还有base64URL
//1.将接到的文件blob转换成base64即可 【fileReader】
//2. 将base64给到对应的预览工具

word和excel

一、 excel

解析内容

  1. 通用 - xlsx
  2. vue - xlsx
  3. react - xlsx

预览

  1. 通用 - xlsx
  2. vue - @vue-office/excel
  3. react - react-file-viewer

场景一:将excel转换为数据

  1. 本地选择文件 -> 读取为前端html和数据对象
使用xlsx库
import {read,writeFile,utils} from 'xlsx'

1. 选择文件后,获取文件对象:let file = e.target.files[0]
2. 将blob对象转换为arrayBuffer,并且调用read方法生成book对象:
    file.arrayBuffer().then(res=>{
        const wb = read(res)  // book对象,代表整个excel
        
        //比如,想操作excel中的sheet1表
        const sheet1 = wb.Sheets.Sheet1
        
        //为了方便前端操作,可以通过utils方法对sheet1对象进行转换
        const data = utils.sheet_to_json(sheet1)  // 可以直接读成js数组
        const _html = utils.sheet_to_html(sheet1)  // 可以转换成html, 直接将_html给 v-html就好 【v-html = '_html'】
        
    })
  1. 请求后端接口 -> 读取为前端html和数据对象
// 为了能从接口获取到blob数据,需要设置响应头
axios.get('http://api',{responseType:'blob'}).then(res=>{

        //下面这些操作和上面本地选择的方式一样
        file.arrayBuffer().then(res=>{
            const wb = read(res)  
        
            const sheet1 = wb.Sheets.Sheet1
        
            const data = utils.sheet_to_json(sheet1) 
            const _html = utils.sheet_to_html(sheet1)
        })
})

场景二:将数据转换为excel

  1. 把前端的一个table dom转化为excel
  2. 把一个前端对象转换为excel
方式一:
// 数据/html 需要先转换为sheet对象,然后创建workbook,之后输出为excel文件
let a=[
    {name:'sss',id:1000,score:99},
    {name:'rrr',id:2000,score:99}
    {name:'hhh',id:3000,score:99}
]

const ws = utils.json_to_sheet(a); // worksheet,excel中的一个sheet表

const wb = utils.book_new()   // 创建一个空的book对象, 即创建一个空的excel文件  

utils.book_append_sheet(wb,ws,'sheet1') //将sheet表加入到book对象(excel)中

writeFile(wb,'text1.xlsx') // 将book对象 写入文件


方式二:将table dom 转换为excel
//1. 获取table dom 元素
const tableDom = this.$refs.excelTable
//2. 将tableDom 转换为sheet
const ws=utils.table_to_sheet(tableDom)
//3. 创建一个新的workbook
const wb = utils.book_new()
//4. 将内容加入到book对象(excel)中,通过下面这个方法,上面创建的空book对象会被写入内容
utils.book_append_sheet(wb,ws,'sheet2')
// 创建这个文件
writeFile(wb,'text2.xlsx') 
const wb=utils.table_to_book(tableDom)

// 总结:xlsx库预览不是太好,因为只能使用html,样式太丑
// 可以使用@vue-office/excel插件
// 还有其他兄弟库【word:@vue-office/docx,pdf:@vue-office/pdf】

@vue-office/excel插件的使用

import vueofficeExcel from '@vue-office/excel'

vueofficeExcel 这是一个组件

// 使用
<vueofficeExcel :src='' style="height:500px">  // 这样就可以预览了,src为空 是显示一个空的表格

// 赋值src(可以直接赋值文件路径,如'/a.xlsx',也可以直接显示), 就可以显示内容
// src有两种形式:1. 线上的文件路径地址,2. dataURL(dataURL即base64)【如果是二进制文件对象,将其转换为base64就好】

// 读取文件为base64的操作,比如在input中的change事件中
let file = e.target.files[0]
const fr = new FileReader()
fr.readAsDataURL(file);
fr.onload = (e)=>{
   src = e.target.result // 这个src就是转换为base64的结果
}

二、word

解析内容

  1. 通用 - docxtemplater
  2. vue - docxtemplater
  3. react - docxtemplater

预览

  1. 通用 - mammoth 或 docx-preview
  2. vue - @vue-office/docx
  3. react - react-file-viewer

@vue-office/docx插件的使用

import vueofficedocx from '@vue-office/docx'

vueofficedocx 这是一个组件

// 使用
<vueofficedocx :src='wordPath' style="height:500px">  // 这样就可以预览了,wordPath为空 是显示一个空的表格

// 赋值src(可以直接赋值文件路径,如'/a.docx',也可以直接显示), 就可以显示内容
// src有两种形式:1. 线上的文件路径地址,2. dataURL(dataURL即base64)【如果是二进制文件对象,将其转换为base64就好】

// 读取文件为base64的操作,比如在input中的change事件中
let file = e.target.files[0]
const fr = new FileReader()
fr.readAsDataURL(file);
fr.onload = (e)=>{
   wordPath = e.target.result // 这个src就是转换为base64的结果
}

// 总结 : 和excel的类似,甚至一摸一样

docx-preview 通用插件方法

import {renderAsync} from 'docx-preview'

// 在input中的change事件中
let file = e.target.files[0]
let dom  = this.$ref.xxx //通过ref获取到的dom
//renderAsync函数可以直接接收blob或arrayBuffer
renderAsync(file,dom)