1.介绍
效果如下图
2.思路
文件上传的两种实现方式
1.From形式
- <form
- method="post"
- enctype="multipart/from-data"
- action="api/upload"
- >
- <input type="file name="file">
- <button type="submit">Submit</button>
- </form>
form的method属性指定为 "post" 请求,通过HTML表单发送数据给服务器,并返回服务器的修改结果,在这种情况下Content-Type是通过在<form>元素中设置正确的enctype属性。
form的enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。
- application/x-www-form-urlencoded(默认值):表示在发送前编码所有字符,数据被编码成以"&"分隔的键值对,同时以"="分隔键和值,("name=seven&age=19")。不支持二进制数据。
- multipart/form-data:支持二进制数据(上传文件时必须指定)
2.JavaScript异步请求形式
我们知道 FormData 接口提供了一种表示表单数据的键值对 key/value 的构造方式,并且可以轻松的将数据通过XMLHttpRequest.send()方法发送出去,本接口和此方法都相当简单直接。如果送出时的编码类型被设为 "multipart/form-data",它会使用和表单一样的格式。
- var formdata = new FormData(); // 创建FormData对象
- formdata.append("name","laotie"); // 通过append()方法添加新的属性值
- ... // 更多方法请点下面链接
FormData接口
3.生命周期
上传组件也有它的生命周期
beforeUpload --> uploading --> fileUploaded 或者 uploadedError
4.代码草稿
本例中采用js异步请求的方式开发上传组件
- <input type="file" name="file" @change.prevent="handleFileChange">
- // 创建一个file类型的input,用于触发文件上传,后面可以把input隐藏掉,自定义好看的样式
- // 自定义样式的时候可以用slot区分不同上传状态的样式(loading,success,defult)
- const handleFileChange = (e:Event)=>{
- const target = e.target as HTMLInputElement
- const files = Array.from(target.files)// 注意这里取得的是一个类数组
- if(files){
- // 取得文件
- const uploadedFile = files[0]
-
- if(!validateFormat) return
- // ...这里只是提供一种思路,具体校验不再讲述
- // 在这里做一些上传文件前的校验,比如文件格式,大小等,
- // 不符合要求的话就不在继续发送请求
-
- const formData = new FormData()
- formData.append(uploadedFile.name,uploadedFile)
-
- axios.post('/upload',formData,{
- headers:{
- // 注意设置编码类型
- 'Content-Type': 'multipart/form-data'
- }
- }).then(res=>{
- console.log('上传成功')
- }).catch(error =>{
- // 文件上传失败
- }).finally(()=>{
- // 文件上传完成,无论成功还是失败
- // 这里可以清除一下input.value
- })
- }
- }
5.具体实现
- // Upload.vue
- <template>
- <div class="upload-container">
- <div class="upload-box" @click.prevent="triggerUpload" v-bind="$attrs">
- <slot name="loading" v-if="fileStatus==='loading'">
- <button class="btn btn-primary">上传中</button>
- </slot>
- <slot name="uploaded" v-else-if="fileStatus==='success'" :uploadedData="fileData">
- <button class="btn btn-primary">上传成功</button>
- </slot>
- <slot v-else name="default">
- <button class="btn btn-primary">点击上传</button>
- </slot>
- </div>
- <input type="file" class="file-input d-none" name="file" ref="uploadInput" @change="hanldeInput"/>
- </div>
- </template>
- <script lang="ts">
- import { defineComponent, ref, PropType, watch } from 'vue'
- import axios from 'axios'
- type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
- type FunctionProps = (file:File) => boolean
- export default defineComponent({
- name: 'Upload',
- inheritAttrs: false,
- props: {
- // 上传的url
- action: {
- type: String,
- required: true
- },
- // 上传之前的校验,是一个返回布尔值的函数
- beforeUpload: {
- type: Function as PropType<FunctionProps>
- },
- // 上传好的数据,用来判断状态或做初始化展示
- uploadedData: {
- type: Object
- }
- },
- emits: ['file-uploaded-success', 'file-uploaded-error'],
- setup(props, ctx) {
- const uploadInput = ref<null | HTMLInputElement>(null)
- const fileStatus = ref<UploadStatus>(props.uploadedData ? 'success' : 'ready')
- const fileData = ref(props.uploadedData)
- watch(() => props.uploadedData, (val) => {
- if (val) {
- fileStatus.value = 'success'
- fileData.value = val
- }
- })
- const triggerUpload = () => {
- if (uploadInput.value) {
- uploadInput.value.click()
- }
- }
- const hanldeInput = (e:Event) => {
- const target = e.target as HTMLInputElement
- const files = target.files
- console.log(target)
- if (files) {
- const uploadFile = Array.from(files)
- const validateFormat = props.beforeUpload ? props.beforeUpload(uploadFile[0]) : true
- if (!validateFormat) return
- fileStatus.value = 'loading'
- const formData = new FormData()
- formData.append('file', uploadFile[0])
- axios.post(props.action, formData, {
- headers: {
- 'Content-Type': 'multipart/form-data'
- }
- }).then(res => {
- console.log('文件上传成功', res)
- fileStatus.value = 'success'
- fileData.value = res.data
- ctx.emit('file-uploaded-success', res.data)
- }).catch(error => {
- console.log('文件上传失败', error)
- fileStatus.value = 'error'
- ctx.emit('file-uploaded-error', error)
- }).finally(() => {
- console.log('文件上传完成')
- if (uploadInput.value) {
- uploadInput.value.value = ''
- }
- })
- }
- }
-
- return {
- uploadInput,
- triggerUpload,
- hanldeInput,
- fileStatus,
- fileData
- }
- }
- })
- </script>
-
使用示例:
- <template>
- <div class="create-post-page">
- <upload
- action="/upload"
- :beforeUpload="beforeUpload"
- :uploadedData="uploadedData"
- @file-uploaded-success="hanldeUploadSuccess"
- class="d-flex align-items-center justify-content-center bg-light text-secondary w-100 my-4"
- >
- <template #uploaded="slotProps">
- <div class="uploaded-area">
- <img :src="slotProps.uploadedData.data.url"/>
- <h3>点击重新上传</h3>
- </div>
- </template>
- <template #default>
- <h2>点击上传头图</h2>
- </template>
- <template #loading>
- <div class="d-flex">
- <div class="spinner-border text-secondary" role="status">
- <span class="sr-only"></span>
- </div>
- </div>
- </template>
- </upload>
- </div>
- </template>
- <script lang="ts">
- import { defineComponent, ref, onMounted } from 'vue'
- import Upload from '../components/Upload.vue'
- import createMessage from '../components/createMessage'
-
- export default defineComponent({
- name: 'CreatePost',
- components: { Upload },
- setup() {
- const uploadedData = ref() //创建一个响应式数据
- let imageId = ''
- onMounted(() => {
- ....
- // 这里有逻辑省略了,取到初始化数据image
- if (image) {
- uploadedData.value = { data: image }
- }
- })
- // 上传前校验,返回布尔值
- const beforeUpload = (file:File) => {
- const res = beforeUploadCheck(file, {
- format: ['image/jpeg', 'image/png'],
- size: 1
- })
- const { error, passed } = res
- if (error === 'format') {
- createMessage('上传图片只能是JPG/PNG格式!', 'error')
- }
- if (error === 'size') {
- createMessage('上传图片大小不能超过1MB', 'error')
- }
- return passed
- }
- // 上传成功后拿到imageId就可以进行后续处理,创建表单啥的
- const hanldeUploadSuccess = (res:ResponeseProps<ImageProps>) => {
- createMessage(`上传图片ID ${res.data._id}`, 'success')
- if (res.data._id) {
- imageId = res.data._id
- }
- }
- return {
- beforeUpload,
- hanldeUploadSuccess,
- uploadedData
- }
- }
- })
- </script>
- <style>
- .create-post-page{
- padding:0 20px 20px;
- }
- .create-post-page .upload-box{
- height:200px;
- cursor: pointer;
- overflow: hidden;
- }
- .create-post-page .upload-box img{
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- .uploaded-area{
- position: relative;
- }
- .uploaded-area:hover h3{
- display: block;
- }
- .uploaded-area h3{
- display: none;
- position: absolute;
- color: #999;
- text-align: center;
- width: 100%;
- top:50%
- }
- </style>
-
以上就是vue 实现上传组件的详细内容,更多关于vue 上传组件的资料请关注w3xue其它相关文章!