最近使用七牛上传的时候遇到了一点小问题:上传时开启七牛图片压缩,发现能够成功请求七牛上传接口但是没有触发complete导致不能获取回调,原因已经找到,因为七牛图片压缩之后的文件是Blob对象,直接上传的话七牛jdk会有问题,将压缩后的Blob对象转换成File对象就行了。
//官方实例
qiniu.compressImage(file, options).then(data => {
const observable = qiniu.upload(data.dist, key, token, putExtra, config)
const subscription = observable.subscribe(observer) // 上传开始
})
// 封装好的七牛上传函数
import * as qiniu from 'qiniu-js'
export default (file,type='image',token,domain)=>{
return new Promise((resolve,reject)=>{
const options = {quality: 0.92,noCompressIfLarger: true}
const config = {
useCdnDomain: true,
region: qiniu.region.z0,
chunkSize: 100,
forceDirect: true,
}
const putExtra = {
fname: file.name,
mimeType: [] || null
};
let key = new Date().getTime() + Math.random(1000) + file.name;
const upload = (f)=>{
let observable = qiniu.upload(f, key, token, putExtra, config);
observable.subscribe({
next: () => {},
error: (err) => {
console.log(err);
reject(err)
},
complete: (res) => {
const resFileObj = {
file_suffix: f.name.replace(/.+\./, ""),
real_url: `${domain}/${res.key}`,
cover_url: type=='video' ? `${domain}/${res.key}?vframe/jpg/offset/1` : ''
}
resolve(resFileObj)
}
})
}
// 图片压缩
if(type=='image'){
qiniu.compressImage(file, options).then(data=>{
const blobToFile = (data) => new File(
[data], file.name,
{type: file.type, lastModified: Date.now()}
)
upload(data.dist instanceof File ? data.dist : blobToFile(data.dist))
})
}else{
upload(file)
}
})
}
---------补充一下在vue组件中的使用方法------------
<template>
<div class="upload-container">
<div class="file-list">
<div
class="img-item"
v-for="(item,index) in fileList"
:key="item.url"
>
<van-image
:src="item.type == 1 ? item.real_url : item.cover_url"
@click="item.type==1 ? viewImage(index) : viewVideo(item)"
/>
<div class="del-tag" @click="delFile(item)">
<van-icon name='cross' />
</div>
</div>
<van-uploader
v-if="uploadShow"
:preview-image="false"
:max-count="limit"
:max-size="fileSize"
:accept="typeMap[type]"
:before-read="beforeRead"
:after-read="afterRead"
upload-icon="plus"
@oversize="onOversize"
/>
</div>
</div>
<div class="video-player" v-if="playVideo" @click.stop="playVideo = false">
<div id="player" @click.stop></div>
<div class="close-btn" @click.stop="playVideo = false">
<van-icon name="cross"/>
</div>
</div>
</template>
<script>
import {ref,reactive,onMounted,nextTick,watch} from 'vue'
import { Uploader, Image, Icon, ImagePreview, Toast} from 'vant';
import Player from 'xgplayer';
import QnUpload from '@/utils/qnUpload.js'
import * as Api from '@/api/imgupload.js'
import { getCookie } from '@/utils/cookie.js'
export default {
components:{
[Uploader.name]: Uploader,
[Image.name]: Image,
[Icon.name]: Icon
},
props:{
type:{
type: String,
default: 'image'
},
size: {
type: Number,
default: 0
},
limit:{
type: Number,
default: 0
},
list:{
type: Array,
default: ()=>[]
},
multiple:{
type: Boolean,
default: false
},
token:{
type: String,
default: ''
},
domain:{
type: String,
default: ''
}
},
emits:['updataList'],
setup(props,{emit}){
let player = ref(null)
let playVideo = ref(false)
let fileSize = ref(0)
let fileList = ref([])
let typeMap = reactive({
'image': 'image/jpeg,image/png',
'video': 'video/*',
'audio': 'audio/*'
})
let uploadShow = ref(true)
onMounted(()=>{
fileSize.value = 1024*1024*props.size
})
watch(
()=>props.list,
(newVal)=>{
fileList.value = newVal
if(newVal.length>=props.limit){
uploadShow.value = false
}else{
uploadShow.value = true
}
},{
immediate: true,
deep: true
}
)
//图片预览
const viewImage = (index)=>{
ImagePreview({
images: fileList.value.map(it=>it.real_url),
startPosition: index,
})
}
//视频预览
const viewVideo = ({real_url=''})=>{
playVideo.value = true
nextTick(()=>{
player.value = new Player({
id: 'player',
url: real_url,
fluid: true,
fitVideoSize: 'auto',
cssFullscreen: true,
autoplay: true,
playsinline: true,
"x5-video-player-type": true
})
player.value.on('ended',()=>{
playVideo.value = false
})
})
}
//超过设置文件大小提示
const onOversize = ()=>{
Toast(`文件大小不能超过${props.size}M`);
}
//上传前置处理
const beforeRead = (file)=> {
return new Promise((resolve,reject)=>{
if(props.type == 'video'){
let url = URL.createObjectURL(file)
let audioElement = new Audio(url)
audioElement.muted = true
audioElement.play().then(()=>audioElement.pause())
audioElement.addEventListener("loadedmetadata", ()=>{
let audioDuration = audioElement.duration;
if(audioDuration>60){
audioElement.muted = false
Toast(`仅支持上传1分钟时长以内视频`)
reject()
}else{
audioElement.muted = false
resolve(file)
}
})
}else{
if (!['image/jpeg','image/png'].includes(file.type)) {
Toast('所选文件格式不支持');
reject()
}else{
resolve(file)
}
}
})
}
//自定义上传
const afterRead = async(file)=>{
const resFileObj = await QnUpload(file.file,props.type,props.token,props.domain)
await Api.saveResource({
ws_token: getCookie('ws_token'),
resource: [{...resFileObj}]
})
emit('updataList')
window.socket.sendMessage({
event_name: 'sync_app',
ws_token: getCookie('ws_token')
})
}
//文件删除
const delFile = async(item)=>{
await Api.delResource({
ws_token: getCookie('ws_token'),
id: item.id
})
emit('updataList')
window.socket.sendMessage({
event_name: 'sync_app',
ws_token: getCookie('ws_token')
})
}
return{
fileList,
fileSize,
typeMap,
playVideo,
viewImage,
viewVideo,
onOversize,
beforeRead,
afterRead,
delFile,
uploadShow
}
}
}
</script>
<style lang='less' scoped>
.upload-container{
.file-list{
display: grid;
grid-template-columns: repeat(4, 1fr);
align-content: center;
justify-content: center;
.img-item{
width: 160px;
height: 160px;
border-radius: 10px;
overflow: hidden;
margin-bottom: 10px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
.del-tag{
width: 40px;
height: 40px;
border-radius: 50%;
background: rgba(0,0,0,0.4);
display: flex;
justify-content: center;
align-items: center;
position: absolute;
right: 4px;
top: 4px;
&:deep(.van-icon) {
font-size: 12px;
color: #fff;
}
}
}
&:deep(.van-uploader__upload){
width: 160px;
height: 160px;
background: #EDEDED;
border-radius: 10px;
overflow: hidden;
.van-icon{
font-size: 80px;
font-weight: 700;
color: #fff;
}
}
}
}
.video-player{
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 100;
background: rgba(0,0,0,0.5);
display: flex;
justify-content: center;
align-items: center;
#player{
width: 100% !important;
height: 100% !important;
padding-top: 0 !important;
}
.close-btn{
width: 60px;
height: 60px;
background: rgba(0,0,0,0.4);
border-radius: 50%;
position: absolute;
right: 20px;
top: 20px;
display: flex;
justify-content: center;
align-items: center;
.van-icon{
font-size: 28px;
color:#fff;
}
}
}
</style>