vue 實現上傳組件
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 上傳組件的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 原生JS實現文件上傳
- vue封裝el-upload批量上傳隻請求一次接口
- 解析element-ui中upload組件傳遞文件及其他參數的問題
- Vue+Axios實現文件上傳自定義進度條
- 詳解Vue ElementUI手動上傳excel文件到服務器