很多人都说文件上传怎么这么难和麻烦,其实文件上传很容易实现,就分为两步。获取文件,然后发送出去。不多逼逼,下面就开始讲述文件上传的各个骚操作
一、文件上传步骤
- 获取文件
- 发送文件
看上去很简单,但是细分起来还是有很多地方可以讲述的。比如限制文件上传格式,文件上传大小,多文件上传等。
二、文件获取
1.普通获取文件
文件获取的本质就是通过input元素获取。下面的例子全部都用vue3实现。 input元素,设置属性type为file
html
<template>
<div>
<input type="file"/>
</div>
</template>
会出现以下的效果
此时选择完文件并且确定后,会触发input
元素的change
事件,通过event.target.files
可以获取到文件列表。因为我们此时只上传了一个文件,所以我们通过event.target.files[0]
获取到这个文件。
vue
<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对象,对象的属性如下
包含最近修改时间、文件名、文件类型、文件大小等属性,file对象其实就是增加了这些额外属性的blob对象,也就是说file对象的原型是blob对象,关于blob(binary large object)对象,不详细讲,可以自行去MDN查询。
2.点击获取文件
只通过input元素获取文件,只能说是丑不拉几。所以我们有了第二种获取文件的方式,本质上也是第一种方式。这种方式非常简单,我们只需要把input元素设置为display:none,并且获取到input元素,增加一个按钮触发input元素的点击事件即可。
vue
<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>
此时我们发现那丑丑的界面就不见了,我们只需要优雅的点击这个按钮就能选择文件了。
3.拖拽获取文件
拖拽获取文件看起来不太简单,但是操作起来还是非常简单的,首先,我们创建一个div,把图片文件拖拽进来,你会发现,文件在浏览器打开了。别慌,这是拖拽的默认行为,我们只需要阻止这个默认行为就行,拖拽会触发很多事件,如drop
、dragstart
、dragend
、dragover
html
<div
class="w-[100px] h-[100px] bg-slate-600"
@drop.prevent="handlerDrop"
@dragover.prevent=""
>
DragIn
</div>
这里我们只需要阻止drop
和dragover
的默认行为就行,只有这两个会打开我们拖拽进来的文件。
我们如何获取这个文件呢,上面也可以看到我们在drop
事件绑定了一个函数。 通过drop
事件的event.dataTransfer.files
可以获取到拖拽进来的文件列表。
js
function handlerDrop(event) {
console.log(event.dataTransfer.files);
}
至此我们已经学会了三种常用的获取文件的方式。
二、上传文件
上传文件就来到了网络请求的部分了,来到网络请求,就得请出我们的老东西Axios
。文件上传的时候,有两种常见的表单数据格式,一种是application/x-www-form-urlencoded
和multipart/form-data
,下面将分别介绍一下
1.application/x-www-form-urlencoded
第一种格式,在这种编码格式下,数据被格式化为键值对,并使用等号(=
)连接键和值,不同的键值对之间用与号(&
)分隔。空格会被转换为加号(+
),特殊字符会被转义为 %
后跟两位十六进制数。用户最终会获得 username=user123&password=pass456
这样的格式。以这种方式上传的话我们只能把数据通过base64编码成文本数据传输给服务端。
此时我们需要这样配置axios
js
//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就可以直接上传了,后端只需要把收到的文件名和文件字符串转为文件保存起来。这样就完成了最简单的上传文件了。
vue
<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的属性值我们可以实现图片文件的预览。
vue
<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>
这就是效果图,我们可以直接预览选择的图片。
2.multipart/form-data
第二种方式就是以formdata的格式上传了,这个方式是上传文件最常用也是最简单方法。FormData
适用于包含文件上传等二进制数据的表单。当表单中包含文件上传元素时,可以使用 FormData
来构建请求,因为它支持二进制数据。 首先需要把Content-Type
换成multipart/form-data
。
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'] = 'multipart/form-data'
export default instance
如何构建formData数据呢,需要用到FormData这个对象,通过这个对象能很方便的创建formData格式的数据。此时我们不用将file对象转换为base64编码的文本数据,直接可以上传file对象。
vue
<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>
至此我们已经学会了全部上传文件的流程了,剩下的就是切片上传、秒传、断点续传等了