uni-app實(shí)現(xiàn)文件上傳功能
目前找到的比較好用的一款第三方插件
文件上傳插件地址 https://ext.dcloud.net.cn/plugin?id=1015
插件下載選擇下載示例項(xiàng)目zip 孽亲,可以直接運(yùn)行項(xiàng)目查看效果
目錄結(jié)構(gòu)如下
index.vue,是使用文件上傳功能的當(dāng)前頁面
@up-success="onSuccess" 是文件上傳成功以后回傳的數(shù)據(jù)
<template>
<view>
<l-file ref="lFile" @up-success="onSuccess"></l-file>
<view class="padding text-center">
<view class="padding">
<button @tap="onUpload">上傳</button>
</view>
</view>
</view>
</template>
<script>
import lFile from '@/components/l-file/l-file.vue'
export default {
components:{lFile},
data() {
return {
}
},
methods: {
/* 上傳 */
onUpload() {
this.$refs.lFile.upload({
// #ifdef APP-PLUS
currentWebview: this.$mp.page.$getAppWebview(),
// #endif
//非真實(shí)地址尚胞,記得更換
url: 'https://www.example.com/upload',
//默認(rèn)file,上傳文件的key
name: 'uploadFile',
// header: {'Content-Type':'類型','Authorization':'token'},
//...其他參數(shù)
});
},
onSuccess(res) {
console.log('上傳成功回調(diào)=====33====',JSON.stringify(res));
uni.showToast({
title: JSON.stringify(res),
icon: 'none'
})
}
}
}
</script>
l-file.vue,是文件上傳功能的封裝組件,最后加載的是index.html文件
wv.overrideUrlLoading 監(jiān)聽返回的文件上傳結(jié)果。
getRequest(url) {
let theRequest = new Object();
let index = url.indexOf("?");
if (index != -1) {
let str = url.substring(index+1);
let strs = str.split("&");
for(let i = 0; i < strs.length; i ++) {
theRequest[strs[i].split("=")[0]]=unescape(strs[i].split("=")[1]);
}
}
return theRequest;
},
appChooseFile({currentWebview,url,name = 'file',header,...formData} = {}) {
// #ifdef APP-PLUS
let wv = plus.webview.create("","/hybrid/html/index.html",{
'uni-app': 'none', //不加載uni-app渲染層框架死遭,避免樣式?jīng)_突
top: 0,
height: '100%',
background: 'transparent'
},{
url,
header,
key: name,
...formData,
});
wv.loadURL("/hybrid/html/index.html")
currentWebview.append(wv);
wv.overrideUrlLoading({mode:'reject'},(e)=>{
let {fileName,id} = this.getRequest(e.url);
return this.onCommit(
this.$emit('up-success',{fileName,data: {id,statusCode: 200}})
);
});
// #endif
}
index.html 源碼
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title class="title">[文件管理器]</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<style type="text/css">
.content {background: transparent;}
.fixed {position: fixed;bottom: 0;left: 0;right: 0;width: 100%;}
.content .mask {top: 0;background: rgba(0,0,0,.4);z-index: 90;}
.content .file-content {z-index: 91;height: 60px;background: #fff;text-align: center;}
.btn {position: relative;}
.btn .file {position: absolute;z-index: 93;left: 0;right: 0;top: 0;bottom: 0;height: 60px;width: 100%;opacity: 0;}
.btn-bg {margin-top: 10px;background: #0066CC;color: #fff;width: 80%;height: 40px;border: 0;border-radius: 5px;}
.tis {top: 0;z-index: 95;display: none;justify-content: center;align-items: center;}
.tis .tis-content {background: #fff;width: 60%;border-radius: 10px;padding: 20px 0;}
.tis .tis-content img {width: 50px;height: 50px;}
.tis-progress {margin: 10px 0;color: #999;}
.cancel-btn {margin-top: 30px;height: 30px;line-height: 1;padding: 0 2em;background: #e3e3e3;color: #898989;border: 0;border-radius: 5px;}
</style>
</head>
<body>
<div class="content">
<div class="fixed mask"></div>
<div align="center" class="fixed tis">
<div class="tis-content">
<div>
<img src="../../static/logo.png" >
</div>
<div class="tis-progress">
努力上傳中..
</div>
<div class="cancel">
<button type="button" class="cancel-btn">取消上傳</button>
</div>
</div>
</div>
<div class="fixed file-content">
<div class="btn">
<button type="button" class="btn-bg">打開文件管理器</button>
<input class="file" type="file" />
</div>
</div>
</div>
<script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
<script src="js/h5-uploader.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
h5-uploader.js 源碼
負(fù)責(zé)h5代碼上傳文件及其進(jìn)度展示圣猎,及上傳結(jié)果回傳
let mask = document.querySelector(".mask");
let fileDom = document.querySelector(".file");
let tis = document.querySelector(".tis");
let progress = document.querySelector(".tis-progress");
let cancel = document.querySelector(".cancel-btn");
let createUpload = (file, url, key='file', header = {},data = {}) => {
console.log(`
上傳地址:${url}\n
請(qǐng)求頭:${JSON.stringify(header)}\n
參數(shù):${JSON.stringify(data)}
`);
if (!url) {return;}
tis.style.display = 'flex';
let formData = new FormData();
formData.append(key, file);
for (let keys in data) {
formData.append(keys, data[keys]);
}
let xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
for (let keys in header) {
xhr.setRequestHeader(keys, header[keys]);
}
xhr.upload.addEventListener("progress", function(event) {
if(event.lengthComputable){
let percent = Math.ceil(event.loaded * 100 / event.total) + "%";
progress.innerText = `努力上傳中..${percent}`;
}
}, false);
xhr.ontimeout = function(){
// xhr請(qǐng)求超時(shí)事件處理
progress.innerText = '請(qǐng)求超時(shí)';
setTimeout(()=>{
tis.style.display = 'none';
plus.webview.currentWebview().close();
},1000);
};
xhr.onreadystatechange = (ev) => {
if(xhr.readyState == 4) {
console.log('status:'+xhr.status);
if (xhr.status == 200) {
progress.innerText = '上傳成功';
console.log('返回?cái)?shù)據(jù):'+xhr.responseText);
location.href = `callback?fileName=${file.name}&id=${xhr.responseText}`;
}
else {
progress.innerText = '上傳失敗了';
}
setTimeout(()=>{
tis.style.display = 'none';
plus.webview.currentWebview().close();
},1000);
}
};
xhr.send(formData);
cancel.addEventListener("click", ()=>{
xhr.abort();
plus.webview.currentWebview().close();
});
}
mask.addEventListener("click", () => {
plus.webview.currentWebview().close();
});
document.addEventListener('UniAppJSBridgeReady', () => {
let {url,key,header,formData} = plus.webview.currentWebview();
fileDom.addEventListener('change', (event) => {
let file = fileDom.files[0];
if(file.size > (1024*1024 * 10)) {
plus.nativeUI.toast('單個(gè)文件不能超過10M,請(qǐng)重新上傳');
return;
}
console.log(file.name);
createUpload(file, url, key,header,formData);
}, false);
});