vue 實現上傳元件
阿新 • • 發佈:2021-06-02
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屬性指www.cppcns.com定為 "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 } }) co程式設計客棧nst 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,{ 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
wldxWJR 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 #uwldxWJRploaded="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,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 上傳元件的資料請關注我們其它相關文章!