阿里oss断点续传(vue组件)
发布: 2018-08-14 17:42:02标签: vue
点击查看官方文档 (简单demo)
官网sdk介绍中js使用的库和node使用的是同一个包,ali-oss。
和nodejs不同的是,js中使用需要在控制台设置允许跨域,和headers暴露信息
- 注意设置回掉callback中键值对中value必须是string类型
- 获取文件唯一MD5标识,文件过大会出现错误
安装
yarn add ali-oss
# or
npm install ali-oss -S
简易js代码
import OSS from 'ali-oss'
import SparkMD5 from 'spark-md5' // 依赖库,对文件进行md5加密唯一标识
import VProgress from './Progress' // 一个简单的进度条组件
// localstorage缓存上传进度指针,失败后可再次上传
const pointerCache = {
get(file, name) {
const cache = localStorage.getItem(cacheKey)
if (!cache) return null
const cacheJson = JSON.parse(cache)
if (cacheJson.name !== name) return null
cacheJson.file = file
return cacheJson
},
set(pointer) {
localStorage.setItem(cacheKey, JSON.stringify(pointer))
},
remove() {
localStorage.removeItem(cacheKey)
}
}
export default {
props: {
data: {
type: Object,
default() {
return {
region: '',
accessKeyId: '',
accessKeySecret: '',
bucket: ''
}
}
},
multiple: {
type: Boolean,
default: true
}
},
data() {
return {
files: []
}
},
methods: {
async changFile() {
const files = this.$refs.input.files || []
for (let i = 0; i < files.length; i++) {
const file = files[i]
const name = await this.getName(file)
if (!this.files.some(item => item.name === name)) {
this.files.push({
file,
percent: 0,
name
})
}
}
console.log(this.files)
},
getName(file) {
return new Promise(resolve => {
const {name} = file
const suffix = name.substr(name.lastIndexOf('.'))
const spark = new SparkMD5.ArrayBuffer()
const reader = new FileReader()
reader.readAsArrayBuffer(file)
// Indicator.open() // loading 文件过大的时候读取时间非常长
reader.addEventListener('load', (e) => {
spark.append(e.target.result);
// Indicator.close()
resolve(spark.end() + suffix) // 文件md5加密,放置重复上传
})
})
},
async uploadAll() {
if (this.files.length === 0) {
// Toast('请选择文件')
return
}
for (let i = 0; i < this.files.length; i++) {
const {file, name} = this.files[i]
await this.upload(file, name, i)
}
},
async upload(file, name, index) {
const that = this
const client = new OSS(this.data) // 阿里文件上传对象
let checkpoint = pointerCache.get(file, name) // 断点续传指针
// 失败后尝试两次
for (let i = 0; i < 2; i++) {
try {
const result = await client.multipartUpload(name, file, {
checkpoint,
async progress(percentage, cpt) { // 上传进度
console.log(percentage)
that.files[index].percent = parseInt(percentage * 100, 10)
pointerCache.set(cpt)
checkpoint = cpt
},
})
pointerCache.remove()
console.log('上传成功:', result)
break // break if success
} catch (e) {
console.log('上传失败:', e)
}
}
},
},
components: {
VProgress
}
}
html部分
<template>
<div class='upload-container'>
<button class='btn main-btn' @click='uploadAll'>开始上传</button>
<div class='upload-panel'>
<p>将文件拖到此处,或 <span class='color-main'>点击上传</span></p>
<input :multiple='multiple' @change='changFile' class='file-input' ref='input' type='file'>
</div>
<table v-if='files.length' class='table'>
<tr>
<th>原文件</th>
<th>md5文件</th>
<th>文件大小</th>
<th>上传</th>
<th>状态</th>
</tr>
<tr v-for='(item) in files' :key='item.file.name'>
<td>{{item.file.name}}</td>
<td>{{item.name}}</td>
<td>{{item.file.size}}</td>
<td>
<v-progress :percent='item.percent'></v-progress>
</td>
<td>
<div class='btn opacity' :class='{complete: item.percent === 100}'>{{item.percent === 100 ? '完成' : '等待'}}</div>
</td>
</tr>
</table>
</div>
</template>
css部分
<style lang='scss' scoped>
.upload-container {
overflow: hidden;
margin: 20px;
}
.opacity {
opacity: .7;
}
.color-main {
color: #2989d8
}
.upload-panel {
height: 400px;
border: 1px dashed #ccc;
position: relative;
display: flex;
justify-content: center;
align-items: center;
background: #fcfcfc;
border-radius: 15px;
&:hover {
border-color: #2989d8;
}
}
.file-input {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
}
.btn {
display: inline-block;
font-weight: normal;
text-align: center;
vertical-align: middle;
cursor: pointer;
white-space: nowrap;
padding: 6px 12px;
line-height: 1.42858;
border-radius: 4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
color: #fff;
background-color: #2989d8;
border-color: #2989d8;
margin: 20px 0;
font-size: 14px;
&.main-btn {
font-size: 20px;
}
&.complete {
background-color: #ccc;
border-color: #ccc;
}
}
.table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
text-align: center;
td, th {
border: 1px solid #dfdfdf;
padding: 10px 20px;
}
}
div {
justify-content: center;
}
</style>