VUE學(xué)習(xí)之Element-ui文件上傳實(shí)例詳解
引言
對于文件上傳,在開發(fā)主要涉及到以下兩個(gè)方面:
單個(gè)文件上傳和表單一起實(shí)現(xiàn)上傳(這種情況一般都是文件上傳之后,后端返回保存在服務(wù)器的文件名,最后和我們的表單一起上傳)
單文件上傳
element-ui中的el-upload組件默認(rèn)發(fā)送post請求,在使用upload組件自動(dò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文件,且不超過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 上傳成功之后的鉤子,可以用來清除表單校驗(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” 提示說明文字
和表單一起上傳
需求:需同時(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對象,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文件,且不超過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: "請輸入名稱", trigger: "blur"}, {max: 50, message: "最長可輸入50個(gè)字符", trigger: "blur"}, {validator: isvalidatName, trigger: "blur" }, ], file: [ {required: true, message: "請選擇上傳文件", 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", // 這里寫上傳文件的地址 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)證通過后使用組件自帶的方法觸發(fā)上傳事件 this.$refs.upload.submit() } else { return false; } }); }, // 成功上傳文件 upFile(res, file) { if (res.status == 200) { // 文件上傳成功后的回調(diào),比如一些提示信息或者頁面跳轉(zhuǎn)都寫在這里 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è)證書`); }, // 移除文件 handleRemove(res, file, fileList) { this.$message.warning(`移除當(dāng)前${res.name}證書,請重新選擇證書上傳!`); } }
因?yàn)槲募蟼魇褂玫氖荂ontent-Type: multipart/form-data;攜帶參數(shù)使用的是FormData格式,我們需要把表單數(shù)據(jù)轉(zhuǎn)換一下格式,后臺(tái)才能接收到。
將body的格式進(jìn)行轉(zhuǎn)換一下:
- JSON.stringify() 方法將一個(gè) JavaScript 對象或值轉(zhuǎn)換為 JSON 字符串
- JSON.parse()可以將JSON字符串轉(zhuǎn)為一個(gè)對象。
數(shù)據(jù)的編碼方式
在此之前可以先了解一下文件上傳的編碼方式
http協(xié)議規(guī)定 POST 提交的數(shù)據(jù)必須放在消息主體(entity-body)中,但協(xié)議并沒有規(guī)定數(shù)據(jù)必須使用什么編碼方式。實(shí)際上,開發(fā)者完全可以自己決定消息主體的格式,只要最后發(fā)送的 HTTP 請求滿足上面的格式就可以。
但是,數(shù)據(jù)發(fā)送出去,還要服務(wù)端解析成功才有意義。一般服務(wù)端語言如 php、python ,java等,以及它們的 framework,都內(nèi)置了自動(dòng)解析常見數(shù)據(jù)格式的功能。服務(wù)端通常是根據(jù)請求頭(headers)中的 Content-Type 字段來獲知請求中的消息主體是用何種方式編碼,再對主體進(jìn)行解析。
前端HTTP發(fā)POST請求攜帶參數(shù)與后端接口接收參數(shù)
補(bǔ)充:代理的使用
action:代表上傳的地址,在開發(fā)中一般寫成動(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)境來使用不同的url?因?yàn)殚_發(fā)環(huán)境配置了代理
proxy: { [REQUEST_PRE]: { target: 'http://localhost:88', changeOrigin: true, secure: false, pathRewrite: { ['^' + process.env.VUE_APP_BASE_API]: '' } }, }
而這個(gè)值在開發(fā)環(huán)境中是"/dev-api",在生產(chǎn)環(huán)境是“/”
所以開發(fā)環(huán)境需要在開頭加上"/dev-api",生產(chǎn)環(huán)境則不需要
那為什么只有這一個(gè)要單獨(dú)處理,其他的url不需要呢?因?yàn)槠渌膗rl是通過axios請求的,axios中配置了baseUrl,所以其他的url不需要單獨(dú)處理了
什么是代理?為什么要用代理
因?yàn)殚_發(fā)環(huán)境是配置了代理的,代理就是通過一個(gè)特殊的網(wǎng)絡(luò)服務(wù)去訪問另一個(gè)網(wǎng)絡(luò)服務(wù)的一種間接訪問方式。像我們不能直接訪問國外的網(wǎng)站,只能使用VPN,就是是使用了代理。
那么前端為什么要代理?
- 前端應(yīng)用的要能訪問,那必須是放在服務(wù)器上,(服務(wù)器可以是nginx、node.js、apache、tomcat等),我們本地vue開發(fā)就是用nodejs啟動(dòng)了一個(gè)服務(wù)。
- 由于瀏覽器的同源策略(協(xié)議,IP,端口號都相同為同源),禁止網(wǎng)站向非同源的服務(wù)器發(fā)送ajax異步請求,也就是跨域。
因此使用代理來解決跨域問題
代理的使用
Vue代理配置
vuecli3的代理設(shè)置在vue.config.js->devServer->proxy
vite構(gòu)建的vue代理設(shè)置在 vite.config.js->server->proxy
(Vite 是 vue 的作者尤雨溪在開發(fā) vue3.0 的時(shí)候開發(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: { // 簡單寫法 "/normal":{ target: 'http://localhost:80', ws: true, // 允許websocket代理 changeOrigin: true //允許跨域 }, // 變量寫法 [REQUEST_PRE]: { target: 'http://localhost:88', ws: true, changeOrigin: true }, // 重寫,當(dāng)訪問http://localhost:80/req/getData // 實(shí)際上訪問的是 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、后面的反斜杠要保持一致,雖然對接口調(diào)用沒有影響,但是對代理文件的相對路徑有影響
如 /req,target就寫 http://localhost:8888
如 /req/,target就寫 http://localhost:8888/
2、不管是vuecli還是vite,同前綴代理是有前后順序的,只生效前面的
比如當(dāng)你調(diào)用 localhost:8080/api/other/getData
下面的寫法會(huì)代理到 8888
'/api': 'http://localhost:8888', '/api/other': 'http://localhost:88'
下面的寫法會(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:對象/函數(shù),重寫目標(biāo)的 url 路徑。對象鍵將用作正則表達(dá)式來匹配路徑。
// 重寫路徑 pathRewrite: { '^/old/api' : '/new/api' } // 刪除路徑 pathRewrite: { '^/remove/api' : '' } // 添加基本路徑 pathRewrite: { '^/' : '/basepath/' } // 自定義重寫 pathRewrite: function ( path , req ) { return path . 替換('/api' , '/base/api' ) } // 自定義重寫,返回 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è)屬性,但是沒有 NODE_ENV 這個(gè)屬性
- NODE_ENV這個(gè)變量并不是 process.env 直接就有的,而是通過設(shè)置得到的。這個(gè)變量的作用是:我們可以通過判斷這個(gè)變量區(qū)分開發(fā)環(huán)境或生產(chǎn)環(huán)境。
- 當(dāng)我們設(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue移動(dòng)端實(shí)現(xiàn)手機(jī)左右滑動(dòng)入場動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了vue移動(dòng)端實(shí)現(xiàn)手機(jī)左右滑動(dòng)入場動(dòng)畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08vue3中g(shù)etCurrentInstance示例講解
這篇文章主要給大家介紹了關(guān)于vue3中g(shù)etCurrentInstance的相關(guān)資料,文中還介紹了Vue3中關(guān)于getCurrentInstance的大坑,需要的朋友可以參考下2023-03-03Vue-input框checkbox強(qiáng)制刷新問題
這篇文章主要介紹了Vue-input框checkbox強(qiáng)制刷新問題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04簡單實(shí)現(xiàn)Vue的observer和watcher
這篇文章主要教大家如何簡單實(shí)現(xiàn)Vue的observer和watcher,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12如何使用 Vue Router 的 meta 屬性實(shí)現(xiàn)多種功能
在Vue.js中,Vue Router 提供了強(qiáng)大的路由管理功能,通過meta屬性,我們可以在路由定義中添加自定義元數(shù)據(jù),以實(shí)現(xiàn)訪問控制、頁面標(biāo)題設(shè)置、角色權(quán)限管理、頁面過渡效果,本文將總結(jié)如何使用 meta 屬性來實(shí)現(xiàn)這些常見的功能,感興趣的朋友一起看看吧2024-06-06