【前端】-【前端文件操作与文件上传】-【前端接受后端传输文件指南】

发布于:2024-05-09 ⋅ 阅读:(29) ⋅ 点赞:(0)

前端文件操作与文件上传

一、前端文件上传有两种思路:

  1. 二进制blob传输:典型案例是formData传输,相当于用formData搭载二进制的blob传给后端
  2. base64传输:转为base64传输,后端再将base64转回来。简便、耗时

二、与文件相关的对象

  1. files:通过input标签读过来的文件对象,是blob的子类。
  2. blob:不可变的二进制内容,包含很多操作方法,切片上传、断点续传都是基于blob的
  3. formData:用于后端传输的对象。files是一个前端的对象,不能直接传给后端,所以我们需要一个前后端都认可的载体来传递文件,这个载体就是formData。formData就像一辆汽车用来搭载files,这样才能让文件以二进制的形式传到后端
  4. fileReader:多用于把文件读取为某种形式(如base64、text文件)直接传给后端

三、file参数、blob切割文件、FileReader将文件转成base64,浓缩图/文本预览:

<template>
  <div>
    <input type="file" name="file" @change="fileChange" />
    <!-- 缩略图,文本预览 -->
    <img style="width:200px;" :src="imgbase64" />
    <button @click="submit">提交</button>
  </div>
</template>

<script>
import axios from "axios"
import { fstat } from "fs";
let _fileObj;
export default {
  name: 'HelloWorld',
  data() {
    return {
      imgbase64: "",
    }
  },
  methods: {
    fileChange(e) {
       let file = e.target.files[0]// files是个数组
       _fileObj = file;
       // file常用属性:size(大小)、type(类型)、name(文件名)
      if (file.size > 10 * 24 * 24) {
        alert("文件不能大于十兆")
      }
      if (file.type !== 'video/mp4') {
        alert("必须是mp4")
      }
      // 使用blob的slice方法可以切割文件
      let _sliceBlob = new Blob([file]).slice(0, 5000);// 切割二进制的0-5000位
      // 将切割后的Blob对象转成File对象(第二个参数是文件名),之后就可以上传切割后的File对象
      let _sliceFile = new File([_sliceBlob], "test.png");
      
      //将File对象或者Blob对象转成base64或text
      let fr = new FileReader();
      fr.readAsDataURL(_sliceFile);// readAsDataURL是转成base64的方法
      let self = this;
      fr.onload = function () {
        // base64是异步的转换,通过onload方法获得转换结果
        self.imgbase64 = fr.result// 直接将转换结果赋值给img的src,设置src的大小即可获得浓缩图
        console.log(fr.result)// 打印结果见下图一
      }
    },
    async submit() {
    // 这部分的代码后面讲
    	let _formData = new FormData();
        _formData.append("user", "asdasd");
        _formData.append("file", _fileObj)
        axios.post("/xx", _formData);
    }
    }
  }
}
</script>

图一:上传结果打印
四、formData:不仅可以搭载文件,还可以搭载文字、input的其他输入

<form action="xxx" method="post">默认情况下提交为query参数</from>
<form action="xxx" method="post" enctype="multipart/form-data">enctype指定提交为formData</from>

五、文件上传:将blob转成FormData上传

async submit() {
	let _formData = new FormData();
    _formData.append("user", "asdasd");
    _formData.append("file", _fileObj)
    axios.post("/xx", _formData);
}

通过查看网络可以发现:

  1. 上传内容:
    请添加图片描述
  2. 请求头content-Type指定了传输内容为form-data,且boundary指定了分割符
    请添加图片描述
  3. 分隔了两个内容(下面还有一个分割线,即以分割线结尾)
    请添加图片描述
    六、转换关系
    请添加图片描述
  4. 我们直接拿到的是file对象,file对象可以转换成Blob对象,Blob对象也可以转成file对象
  5. file、blob都可以根据FileReader读成base64或text文本
  6. 通过接口传输给后端时,只能传输base64、text文本、formData,所以,如果不把file、blob通过FileReader读成base64或text文本,那么就需要把file或者blob append到formData才能通过接口传输

七、具体功能

  1. 单文件上传(详见前述代码)
  2. 多文件上传:
<template>
  <div>
    <input type="file" name="file" @change="fileChange" multiple />
    <span v-for="item in imgList">{{ item.name }}</span>
    <button @click="submit">提交</button>
  </div>
</template>

<script>
import axios from "axios"
import { fstat } from "fs";
let _fileObj;
export default {
  name: 'HelloWorld',
  data() {
    return {
      imgList: [],
    }
  },
  methods: {
    fileChange(e) {
      //多文件上传
      // multiple的多文件上传其实很不友好,需要用户在选择文件时按住ctrl多选,否则就会变成单选
      // 所以使用imgList数组将用户每次选择的内容都push进去
      if (e.target.files.length > 1) {
        
        this.imgList.concat(e.target.files)
      } else {
        this.imgList.push(e.target.files[0]);
      }
      //切片上传
      _fileObj = e.target.files[0]
    },
    async submit() {
      // 遍历,一个一个上传
      this.imgList.forEach((item) => {
        let _formData = new FormData();
        _formData.append(item.name + "file", item)
        axios.post("/xx", _formData);
      })
    }
  }
}
</script>
  1. 切片上传
<template>
  <div>

    <input type="file" name="file" @change="fileChange" multiple />
    <div>{{ precent }}%</div>
    <button @click="submit">提交</button>
  </div>
</template>

<script>
import axios from "axios"
import { fstat } from "fs";
let _fileObj;
export default {
  name: 'HelloWorld',
  data() {
    return {
      precent: 0
    }
  },
  methods: {
    fileChange(e) {
      //切片上传,在上传的时候进行切片,文件改变时只赋值
      _fileObj = e.target.files[0]
    },
    async submit() {
      let size = 2 * 1024 * 1024;// 每次切片的大小,片为2m
      let fileSize = _fileObj.size;
      let current = 0;// 当前已经传了多少
      // 断点续传,永久记录中断的地方,下次上传时直接从断点开始传
      //localStorage.setItem(_fileObj.name, current);
      while (current < fileSize) {
        let _formData = new FormData();
        _formData.append(_fileObj.name, _fileObj.slice(current, current + size))
        await axios.post("http://localhost:4000/upload",_formData)
        // 进度条可以用axios的onUploadProgress方法,且发送切片时可以并行发送请求,后续可以自行优化
        this.precent = Math.min((current / fileSize) * 100, 100)
        current += size;
      }
    }
  }
}
</script>

File System Access API 允许直接读取、写入或保存对用户设备上的文件和文件夹的更改。从 Chrome 86 开始支持 File System Access API,目前只有基于 Chromium 系列的浏览器全面支持,Safari 部分支持(支持读取,不支持写入和保存),而 Firefox 未支持。

前端接受后端传输文件指南