VUE學(xué)習(xí)之Element-ui文件上傳實(shí)例詳解
引言
對(duì)于文件上傳,在開(kāi)發(fā)主要涉及到以下兩個(gè)方面:
單個(gè)文件上傳和表單一起實(shí)現(xiàn)上傳(這種情況一般都是文件上傳之后,后端返回保存在服務(wù)器的文件名,最后和我們的表單一起上傳)
單文件上傳
element-ui中的el-upload組件默認(rèn)發(fā)送post請(qǐng)求,在使用upload組件自動(dòng)攜帶的請(qǐng)求方式發(fā)送。因此詳情可參考elenent-ui官網(wǎng) Element-UI官網(wǎng)
以下使用手動(dòng)上傳著重介紹一下el-upload的一部分屬性參數(shù)
<el-upload
class="upload-demo"
ref="upload"
action="https://jsonplaceholder.typicode.com/posts/"
:on-preview="handlePreview"
:on-remove="handleRemove"
:auto-upload="false"
list-type="picture">
<el-button slot="trigger" size="small" type="primary">選取文件</el-button>
<el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上傳到服務(wù)器</el-button>
<div slot="tip" class="el-upload__tip">只能上傳jpg/png文件,且不超過(guò)500kb</div>
</el-upload>
<script>
export default {
data() {
return {
fileList: []
};
},
methods: {
handleRemove(file, fileList) {
console.log(file, fileList);
},
handlePreview(file) {
console.log(file);
},
submitUpload() {
this.$refs.upload.submit();
},
}
}
</script>
- data 上傳時(shí)可攜帶的參數(shù),這里后臺(tái)用map接收
- file-list 上傳的文件列表, 例如: [{name: ‘food.jpg’, url: ‘https://xxx.cdn.com/xxx.jpg’}]
- on-success 上傳成功之后的鉤子,可以用來(lái)清除表單校驗(yàn)和文件列表
- on-error 上傳失敗之后的鉤子
- auto-upload 是否在選取文件后立即進(jìn)行上傳,默認(rèn)為true,會(huì)自動(dòng)上傳,false取消自動(dòng)上傳
- list-type 文件列表的類型 text/picture/picture-card 默認(rèn)text
- on-preview 點(diǎn)擊文件列表中已上傳的文件時(shí)的鉤子
- on-change 文件狀態(tài)改變時(shí)的鉤子,添加文件、上傳成功和上傳失敗時(shí)都會(huì)被調(diào)用
- slot=“trigger” 觸發(fā)文件選擇框的內(nèi)容
- slot=“tip” 提示說(shuō)明文字
和表單一起上傳
需求:需同時(shí)給后端傳文件FormData和其他所需參數(shù)
實(shí)現(xiàn)方法一:
關(guān)鍵點(diǎn):el-upload的http-request方法獲取文件File信息和FormData方式傳參
http-request :覆蓋默認(rèn)的上傳行為,可以自定義上傳的實(shí)現(xiàn);函數(shù)的參數(shù)里包含file對(duì)象,filename字段,action字段,以及onProgress,onSuccess,onError三個(gè)函數(shù),可以在自定義上傳中,在不同時(shí)機(jī)調(diào)用這些函數(shù),會(huì)觸發(fā)upload組件傳入的上傳鉤子函數(shù)
<!-- 文件上傳-->
<el-form :rules="rules" :model="dataForm" ref="dataForm" label-width="150px" @submit.native.prevent>
<el-form-item label="名稱:" prop="name">
<el-input type="text" v-model.trim="dataForm.name" clearable></el-input>
</el-form-item>
<el-form-item label="文件:" prop="file" >
<el-upload
class="upload-import"
ref="uploadImport"
:http-request="httpRequest"
action=""
:on-remove="handleRemove"
:file-list="fileList"
:limit="1" <!-- 限制上傳數(shù)量-->
:on-change="handleChange"
:auto-upload="false" <!-- 關(guān)閉自動(dòng)上傳-->
accept="application/zip,.zip"> <!-- 設(shè)置接收的文件類型-->
<el-button v-show="!hasFile" slot="trigger" size="small" type="primary" >選取文件</el-button>
<div slot="tip" class="el-upload__tip">只能上傳zip文件,且不超過(guò)10M</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitUpload">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
<el-button @click="dialogFormVisible = false">取 消</el-button>
</el-form-item>
</el-form>
<script>
export default {
data(){
return {
dataForm: {
name: '',
file: null
},
rules: {
name: [
{required: true, message: "請(qǐng)輸入名稱", trigger: "blur"},
{max: 50, message: "最長(zhǎng)可輸入50個(gè)字符", trigger: "blur"},
{validator: isvalidatName, trigger: "blur" },
],
file: [
{required: true, message: "請(qǐng)選擇上傳文件", trigger: "blur"},
]
},
hasFile: false,
fileList: []
},
methods: {
handleRemove(file, fileList) {
if (!fileList.length) {
this.hasFile = false;
}
this.dataForm.file = null;
},
handleChange(file, fileList) {
if (fileList.length >= 2) {
return;
}
if (fileList.length === 1) {
this.hasFile = true;
}
this.dataForm.file = file;
},
//確定按鈕
//調(diào)用組件upload的submit方法,從而觸發(fā)httpRequest方法,實(shí)現(xiàn)手動(dòng)上傳
submitUpload() {
this.$refs.dataForm.validate(valid => {
if(vaild){
this.$refs.uploadImport.submit();
}
})
},
httpRequest(param) {
let fd = new FormData();
fd.append('file', param.file); // 傳文件
fd.append('name', dataForm.name);
//dataPar.file.raw
axios.post('/interface/importProject', fd , {
headers: {'Content-Type': 'multipart/form-data'},//定義內(nèi)容格式,很重要
timeout: 20000,
}).then(response => {
console.log(res)
//接口成功調(diào)用params上的onSuccess函數(shù),會(huì)觸發(fā)默認(rèn)的successHandler函數(shù)
//這樣可以用自帶的ui等
///params.onSuccess({name: 'eric'})
}).catch(err =>{})
}
})
}
}
}
</script>
實(shí)現(xiàn)方法二:
關(guān)鍵點(diǎn):把表單數(shù)據(jù)作為上傳時(shí)附帶的額外參數(shù)提交給后臺(tái)就好了。
<el-dialog title="添加歌曲" :visible.sync="dialogFormVisible">
<el-form :model="form" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="曲名" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="專輯" prop="introduction">
<el-input v-model="ruleForm.introduction"></el-input>
</el-form-item>
<el-form-item label="歌詞" prop="lyric">
<el-input type="textarea" v-model="ruleForm.lyric"></el-input>
</el-form-item>
<el-form-item label="歌曲文件" prop="introduction">
<el-upload
ref="upload"
:action="url"
:limit="2"
multiple
:with-credentials="true"
:on-success="upFile"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:data="upData"
:auto-upload="false"
accept=".KEY, .CRT">
<img src="/static/images/web/upload.png" alt>
<span>選擇上傳文件</span>
<div slot="tip" class="el-upload__tip">只能上傳.key/.crt文件,且只能上傳兩個(gè)</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitUpload">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
<el-button @click="dialogFormVisible = false">取 消</el-button>
</el-form-item>
</el-form>
</el-dialog>
data() {
return {
url: service.defaults.baseURL + "aaa/bbb", // 這里寫(xiě)上傳文件的地址
form: {
name: "",
introduction: "",
lyric: "",
}
};
},
computed: {
// 這里定義上傳文件時(shí)攜帶的參數(shù),即表單數(shù)據(jù)
upData: function() {
return {
body: JSON.stringify(this.form)
}
}
},
methods: {
add(form) {
this.$refs[form].validate(async valid => {
if (valid) {
// 表單驗(yàn)證通過(guò)后使用組件自帶的方法觸發(fā)上傳事件
this.$refs.upload.submit()
} else {
return false;
}
});
},
// 成功上傳文件
upFile(res, file) {
if (res.status == 200) {
// 文件上傳成功后的回調(diào),比如一些提示信息或者頁(yè)面跳轉(zhuǎn)都寫(xiě)在這里
this.$message.success(res.info);
} else {
this.$message.warning(res.info);
let _this = this;
setTimeout(function() {
_this.$refs.upload.clearFiles();
}, 1000);
}
},
// 上傳文件超出個(gè)數(shù)
handleExceed(files, fileList) {
this.$message.warning(`當(dāng)前只能選擇上傳2 個(gè)證書(shū)`);
},
// 移除文件
handleRemove(res, file, fileList) {
this.$message.warning(`移除當(dāng)前${res.name}證書(shū),請(qǐng)重新選擇證書(shū)上傳!`);
}
}
因?yàn)槲募蟼魇褂玫氖荂ontent-Type: multipart/form-data;攜帶參數(shù)使用的是FormData格式,我們需要把表單數(shù)據(jù)轉(zhuǎn)換一下格式,后臺(tái)才能接收到。

將body的格式進(jìn)行轉(zhuǎn)換一下:
- JSON.stringify() 方法將一個(gè) JavaScript 對(duì)象或值轉(zhuǎn)換為 JSON 字符串
- JSON.parse()可以將JSON字符串轉(zhuǎn)為一個(gè)對(duì)象。


數(shù)據(jù)的編碼方式
在此之前可以先了解一下文件上傳的編碼方式
http協(xié)議規(guī)定 POST 提交的數(shù)據(jù)必須放在消息主體(entity-body)中,但協(xié)議并沒(méi)有規(guī)定數(shù)據(jù)必須使用什么編碼方式。實(shí)際上,開(kāi)發(fā)者完全可以自己決定消息主體的格式,只要最后發(fā)送的 HTTP 請(qǐng)求滿足上面的格式就可以。
但是,數(shù)據(jù)發(fā)送出去,還要服務(wù)端解析成功才有意義。一般服務(wù)端語(yǔ)言如 php、python ,java等,以及它們的 framework,都內(nèi)置了自動(dòng)解析常見(jiàn)數(shù)據(jù)格式的功能。服務(wù)端通常是根據(jù)請(qǐng)求頭(headers)中的 Content-Type 字段來(lái)獲知請(qǐng)求中的消息主體是用何種方式編碼,再對(duì)主體進(jìn)行解析。
前端HTTP發(fā)POST請(qǐng)求攜帶參數(shù)與后端接口接收參數(shù)
補(bǔ)充:代理的使用
action:代表上傳的地址,在開(kāi)發(fā)中一般寫(xiě)成動(dòng)態(tài)綁定的屬性,在計(jì)算屬性中決定其最終值。
<el-upload
:action="uploadURL">
</el-upload>
<script>
export default {
data() {
return {
uploadURL: '',
};
},
computed: {
uploadURL() {
if(process.env.NODE_ENV === 'production'){
return '/api/uploadFile'
}
return '/dev-api/api/uploadFile'
}
}
}
</script>
為什么要區(qū)分運(yùn)行環(huán)境來(lái)使用不同的url?因?yàn)殚_(kāi)發(fā)環(huán)境配置了代理
proxy: {
[REQUEST_PRE]: {
target: 'http://localhost:88',
changeOrigin: true,
secure: false,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
},
}
而這個(gè)值在開(kāi)發(fā)環(huán)境中是"/dev-api",在生產(chǎn)環(huán)境是“/”
所以開(kāi)發(fā)環(huán)境需要在開(kāi)頭加上"/dev-api",生產(chǎn)環(huán)境則不需要
那為什么只有這一個(gè)要單獨(dú)處理,其他的url不需要呢?因?yàn)槠渌膗rl是通過(guò)axios請(qǐng)求的,axios中配置了baseUrl,所以其他的url不需要單獨(dú)處理了
什么是代理?為什么要用代理
因?yàn)殚_(kāi)發(fā)環(huán)境是配置了代理的,代理就是通過(guò)一個(gè)特殊的網(wǎng)絡(luò)服務(wù)去訪問(wèn)另一個(gè)網(wǎng)絡(luò)服務(wù)的一種間接訪問(wèn)方式。像我們不能直接訪問(wèn)國(guó)外的網(wǎng)站,只能使用VPN,就是是使用了代理。
那么前端為什么要代理?
- 前端應(yīng)用的要能訪問(wèn),那必須是放在服務(wù)器上,(服務(wù)器可以是nginx、node.js、apache、tomcat等),我們本地vue開(kāi)發(fā)就是用nodejs啟動(dòng)了一個(gè)服務(wù)。
- 由于瀏覽器的同源策略(協(xié)議,IP,端口號(hào)都相同為同源),禁止網(wǎng)站向非同源的服務(wù)器發(fā)送ajax異步請(qǐng)求,也就是跨域。
因此使用代理來(lái)解決跨域問(wèn)題

代理的使用
Vue代理配置
vuecli3的代理設(shè)置在vue.config.js->devServer->proxy
vite構(gòu)建的vue代理設(shè)置在 vite.config.js->server->proxy
(Vite 是 vue 的作者尤雨溪在開(kāi)發(fā) vue3.0 的時(shí)候開(kāi)發(fā)的一個(gè) 基于原生 ES-Module 的前端構(gòu)建工具)
他們的代理配置是一樣的,這里以vuecli3為例:
const REQUEST_PRE = "/api"
module.exports = {
// 其他配置省略
// 本地代理配置
devServer: {
// 默認(rèn)是false,可以將http://localhost:8080 變?yōu)?https://localhost:8080
https: true,
// 代理配置
proxy: {
// 簡(jiǎn)單寫(xiě)法
"/normal":{
target: 'http://localhost:80',
ws: true, // 允許websocket代理
changeOrigin: true //允許跨域
},
// 變量寫(xiě)法
[REQUEST_PRE]: {
target: 'http://localhost:88',
ws: true,
changeOrigin: true
},
// 重寫(xiě),當(dāng)訪問(wèn)http://localhost:80/req/getData
// 實(shí)際上訪問(wèn)的是 http://localhost:8888/module/getData
'/req': {
target: 'http://localhost:8888',
pathRewrite: path => path.replace('/req', '/module'),//replace方法返回一個(gè)替換好的新的字符串
ws: true,
changeOrigin: true
},
},
open: true
}
}
注意(坑點(diǎn)):
1、后面的反斜杠要保持一致,雖然對(duì)接口調(diào)用沒(méi)有影響,但是對(duì)代理文件的相對(duì)路徑有影響
如 /req,target就寫(xiě) http://localhost:8888
如 /req/,target就寫(xiě) http://localhost:8888/
2、不管是vuecli還是vite,同前綴代理是有前后順序的,只生效前面的
比如當(dāng)你調(diào)用 localhost:8080/api/other/getData
下面的寫(xiě)法會(huì)代理到 8888
'/api': 'http://localhost:8888', '/api/other': 'http://localhost:88'
下面的寫(xiě)法會(huì)代理到 88
'/api/other': 'http://localhost:88', '/api': 'http://localhost:8888'
接口調(diào)用
const REQUEST_PRE = '/api'
axios.get({
url: `${REQUEST_PRE}/getData`
}).then(res=>{
console.log(res)
})
3、pathRewrite:對(duì)象/函數(shù),重寫(xiě)目標(biāo)的 url 路徑。對(duì)象鍵將用作正則表達(dá)式來(lái)匹配路徑。
// 重寫(xiě)路徑
pathRewrite: { '^/old/api' : '/new/api' }
// 刪除路徑
pathRewrite: { '^/remove/api' : '' }
// 添加基本路徑
pathRewrite: { '^/' : '/basepath/' }
// 自定義重寫(xiě)
pathRewrite: function ( path , req ) { return path . 替換('/api' , '/base/api' ) }
// 自定義重寫(xiě),返回 Promise
pathRewrite: async function ( path , req ) {
const should_add_something = await httpRequestToDecideSomething ( path ) ;
if ( should_add_something ) path += "something" ;
返回 路徑;
}
process.env.NODE_ENV
- process 是 node 的全局變量,并且 process 有 env 這個(gè)屬性,但是沒(méi)有 NODE_ENV 這個(gè)屬性
- NODE_ENV這個(gè)變量并不是 process.env 直接就有的,而是通過(guò)設(shè)置得到的。這個(gè)變量的作用是:我們可以通過(guò)判斷這個(gè)變量區(qū)分開(kāi)發(fā)環(huán)境或生產(chǎn)環(huán)境。
- 當(dāng)我們?cè)O(shè)置 mode 為 development 或者 production時(shí),webpack會(huì)自動(dòng)的進(jìn)行一些設(shè)置
mode: development --> process.env.NODE_ENV = development mode: production --> process.env.NODE_ENV = production
默認(rèn)情況下 --> process.env.NODE_ENV = production
總結(jié)
到此這篇關(guān)于VUE學(xué)習(xí)之Element-ui文件上傳詳解的文章就介紹到這了,更多相關(guān)VUE Element-ui文件上傳內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue移動(dòng)端實(shí)現(xiàn)手機(jī)左右滑動(dòng)入場(chǎng)動(dòng)畫(huà)
這篇文章主要為大家詳細(xì)介紹了vue移動(dòng)端實(shí)現(xiàn)手機(jī)左右滑動(dòng)入場(chǎng)動(dòng)畫(huà),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08
vue3中g(shù)etCurrentInstance示例講解
這篇文章主要給大家介紹了關(guān)于vue3中g(shù)etCurrentInstance的相關(guān)資料,文中還介紹了Vue3中關(guān)于getCurrentInstance的大坑,需要的朋友可以參考下2023-03-03
Vue-input框checkbox強(qiáng)制刷新問(wèn)題
這篇文章主要介紹了Vue-input框checkbox強(qiáng)制刷新問(wèn)題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04
vue項(xiàng)目如何刷新當(dāng)前頁(yè)面的方法
這篇文章主要介紹了vue項(xiàng)目如何刷新當(dāng)前頁(yè)面的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
簡(jiǎn)單實(shí)現(xiàn)Vue的observer和watcher
這篇文章主要教大家如何簡(jiǎn)單實(shí)現(xiàn)Vue的observer和watcher,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
如何使用 Vue Router 的 meta 屬性實(shí)現(xiàn)多種功能
在Vue.js中,Vue Router 提供了強(qiáng)大的路由管理功能,通過(guò)meta屬性,我們可以在路由定義中添加自定義元數(shù)據(jù),以實(shí)現(xiàn)訪問(wèn)控制、頁(yè)面標(biāo)題設(shè)置、角色權(quán)限管理、頁(yè)面過(guò)渡效果,本文將總結(jié)如何使用 meta 屬性來(lái)實(shí)現(xiàn)這些常見(jiàn)的功能,感興趣的朋友一起看看吧2024-06-06

