文件上传的各个方案及优化(含nodejs后端)

376 阅读5分钟

很多人都说文件上传怎么这么难和麻烦,其实文件上传很容易实现,就分为两步。获取文件,然后发送出去。不多逼逼,下面就开始讲述文件上传的各个骚操作

一、文件上传步骤

  1. 获取文件
  2. 发送文件

看上去很简单,但是细分起来还是有很多地方可以讲述的。比如限制文件上传格式,文件上传大小,多文件上传等。

二、文件获取

1.普通获取文件

文件获取的本质就是通过input元素获取。下面的例子全部都用vue3实现。 input元素,设置属性type为file

<template>
  <div>
    <input type="file"/>
  </div>
</template>

会出现以下的效果
1702922406003.png
此时选择完文件并且确定后,会触发input元素的change事件,通过event.target.files可以获取到文件列表。因为我们此时只上传了一个文件,所以我们通过event.target.files[0]获取到这个文件。

<script setup lang="ts">
import { defineComponent, ref} from "vue";

function fileChange(e: any) {
  const file = e.target.files[0];
  console.log(file);
}
</script>

<template>
  <div>
    <input type="file"/>
  </div>
</template>

我们将会获取到File对象,对象的属性如下

1702925024252.png

包含最近修改时间、文件名、文件类型、文件大小等属性,file对象其实就是增加了这些额外属性的blob对象,也就是说file对象的原型是blob对象,关于blob(binary large object)对象,不详细讲,可以自行去MDN查询。

2.点击获取文件

只通过input元素获取文件,只能说是丑不拉几。所以我们有了第二种获取文件的方式,本质上也是第一种方式。这种方式非常简单,我们只需要把input元素设置为display:none,并且获取到input元素,增加一个按钮触发input元素的点击事件即可。

<script setup lang="ts">
import { defineComponent, ref } from "vue";
//获取到input元素
const inputRef = ref(null);

function fileChange(e: any) {
  const file = e.target.files[0];
  console.log(file);
}

function clickFun() {
    //触发input的点击事件
  inputRef.value.click();
}
</script>

<template>
  <div>
    <input class="hidden" ref="inputRef" type="file" @change="fileChange" />
    <el-button @click="clickFun">选择文件</el-button>
  </div>
</template>

1702926319741.png
此时我们发现那丑丑的界面就不见了,我们只需要优雅的点击这个按钮就能选择文件了。

3.拖拽获取文件

拖拽获取文件看起来不太简单,但是操作起来还是非常简单的,首先,我们创建一个div,把图片文件拖拽进来,你会发现,文件在浏览器打开了。别慌,这是拖拽的默认行为,我们只需要阻止这个默认行为就行,拖拽会触发很多事件,如dropdragstartdragenddragover

    <div
      class="w-[100px] h-[100px] bg-slate-600"
      @drop.prevent="handlerDrop"
      @dragover.prevent=""
    >
      DragIn
    </div>

这里我们只需要阻止dropdragover的默认行为就行,只有这两个会打开我们拖拽进来的文件。
我们如何获取这个文件呢,上面也可以看到我们在drop事件绑定了一个函数。 通过drop事件的event.dataTransfer.files可以获取到拖拽进来的文件列表。

function handlerDrop(event) {
  console.log(event.dataTransfer.files);
}

1703065179821.png

至此我们已经学会了三种常用的获取文件的方式。

二、上传文件

上传文件就来到了网络请求的部分了,来到网络请求,就得请出我们的老东西Axios。文件上传的时候,有两种常见的表单数据格式,一种是application/x-www-form-urlencodedmultipart/form-data,下面将分别介绍一下

1.application/x-www-form-urlencoded

第一种格式,在这种编码格式下,数据被格式化为键值对,并使用等号(=)连接键和值,不同的键值对之间用与号(&)分隔。空格会被转换为加号(+),特殊字符会被转义为 % 后跟两位十六进制数。用户最终会获得 username=user123&password=pass456这样的格式。以这种方式上传的话我们只能把数据通过base64编码成文本数据传输给服务端。
此时我们需要这样配置axios

//fileUpload.js
//用于文件上传的请求
import axios from 'axios'

let instance = axios.create()
instance.defaults.baseURL = 'http://127.0.0.1:3000/dev-api/vue-admin-template'
instance.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded'

export default instance

代码非常简单只是单纯的配置了Content-Type。接下来我们只需要把file对象转为base64编码的字符串就可以上传文件。

首先我们需要把文件对象转为base64编码的字符串,需要用到fileReader这个类,通过创建的示例中的readAsDataURL,我们可以获取到字符串啦,此时调取axios就可以直接上传了,后端只需要把收到的文件名和文件字符串转为文件保存起来。这样就完成了最简单的上传文件了。

<script setup lang="ts">
import { defineComponent, ref, nextTick } from "vue";
import instance from "@/utils/fileUpload";

const base64Url = ref("");

function fileChange(e: any) {
  const file = e.target.files[0];
  
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = async function () {
    base64Url.value = reader.result;
    
    instance({
      url: "upload",
      method: "post",
      data: {
        base64Url: base64Url.value,
        fileName: file.name,
      },
    }).then((res) => {
      console.log(res);
    });
  };
}

const inputRef = ref(null);
function clickFun() {
  inputRef.value.click();
}
</script>

<script lang="ts">
export default defineComponent({
  name: "Index",
});
</script>

<template>
  <div class="m-8">
    <input class="hidden" ref="inputRef" type="file" @change="fileChange" />
    <el-button @click="clickFun">选择文件</el-button>
  </div>
</template>

此刻我们获取到的base64编码还有点妙用,在这也补充一下。

base64URL可以作为img标签的src的属性值我们可以实现图片文件的预览。

<script setup lang="ts">
import { defineComponent, ref, nextTick } from "vue";
import instance from "@/utils/fileUpload";
// 文件base64编码格式
const base64Url = ref("");

function fileChange(e: any) {
  const file = e.target.files[0];
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onload = async function () {
    base64Url.value = reader.result
  };
}

const inputRef = ref(null);

function clickFun() {
  inputRef.value.click();
}
</script>

<template>
  <div class="m-8">
    <input class="hidden" ref="inputRef" type="file" @change="fileChange" />
    <img ref="imageRef" :src="base64Url" />
    <el-button @click="clickFun">选择文件</el-button>
  </div>
</template>

1703179646640.png 这就是效果图,我们可以直接预览选择的图片。

2.multipart/form-data

第二种方式就是以formdata的格式上传了,这个方式是上传文件最常用也是最简单方法。FormData 适用于包含文件上传等二进制数据的表单。当表单中包含文件上传元素时,可以使用 FormData 来构建请求,因为它支持二进制数据。 首先需要把Content-Type换成multipart/form-data

//用于文件上传的请求
import axios from 'axios'

let instance = axios.create()

instance.defaults.baseURL = 'http://127.0.0.1:3000/dev-api/vue-admin-template'
instance.defaults.headers['Content-Type'] = 'multipart/form-data'

export default instance

如何构建formData数据呢,需要用到FormData这个对象,通过这个对象能很方便的创建formData格式的数据。此时我们不用将file对象转换为base64编码的文本数据,直接可以上传file对象。

<script setup lang="ts">
import { defineComponent, ref, nextTick } from "vue";
import instance from "@/utils/fileUpload";

function fileChange(e: any) {
  const file = e.target.files[0];
  
  const formData = new FormData();
  formData.append("file", file);
  
  instance({
    url: "uploadfile",
    method: "post",
    data: formData,
  }).then((res) => {
    console.log(res);
  });
}

const inputRef = ref(null);

function clickFun() {
  inputRef.value.click();
}
</script>

<template>
  <div class="m-8">
    <input class="hidden" ref="inputRef" type="file" @change="fileChange" />
    <el-button @click="clickFun">选择文件</el-button>
  </div>
</template>

至此我们已经学会了全部上传文件的流程了,剩下的就是切片上传、秒传、断点续传等了

还没写完,后续再增加