You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
12 KiB
369 lines
12 KiB
<template>
|
|
<!--上传组件-->
|
|
<el-dialog class="big-file" title="大文件上传" :close-on-click-modal="false" :modal-append-to-body="false" append-to-body :visible.sync="uploadBigVisible">
|
|
<div class="setting-dialog">
|
|
<div class="uploader-big">
|
|
<uploader
|
|
ref="uploader"
|
|
:auto-start="false"
|
|
:options="options"
|
|
:file-status-text="statusText"
|
|
@file-success="fileSuccess"
|
|
@files-added="filesAdded"
|
|
@file-error="onFileError"
|
|
@file-removed="filesRemove"
|
|
>
|
|
<uploader-unsupport />
|
|
<uploader-drop>
|
|
<p>将文件拖到此处,或点击上传</p>
|
|
<uploader-btn single>
|
|
<slot>
|
|
<i class="iconfont icon-tianjiawenjian upload-icon" />
|
|
</slot>
|
|
</uploader-btn>
|
|
<div class="el-upload__tip">上传限制文件大小:最大10GB/个</div>
|
|
<!-- <uploader-btn :attrs="attrs">选择图片</uploader-btn>
|
|
<uploader-btn :directory="true">选择文件夹</uploader-btn> -->
|
|
</uploader-drop>
|
|
<uploader-files />
|
|
<!-- <ul class="file-list">
|
|
<li
|
|
v-for="file in fileList"
|
|
:key="file.id"
|
|
class="file-item"
|
|
:class="`file-${file.id}`"
|
|
>
|
|
<uploader-file
|
|
ref="files"
|
|
:class="'file_' + file.id"
|
|
:file="file"
|
|
:list="true"
|
|
/>
|
|
</li>
|
|
</ul> -->
|
|
</uploader>
|
|
</div>
|
|
<div slot="footer" class="dialog-footer">
|
|
<el-button type="text" @click="handleCloseDialog">取消</el-button>
|
|
<el-button :loading="btnLoading" type="primary" @click="handleUploadConfirm">保存</el-button>
|
|
</div>
|
|
</div>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapGetters } from 'vuex'
|
|
import axios from 'axios'
|
|
import SparkMD5 from 'spark-md5'
|
|
import { getToken } from '@/utils/auth'
|
|
import { getCurrentTime } from '@/utils/index'
|
|
// https://juejin.cn/post/7040817922540830728
|
|
export default {
|
|
props: {
|
|
selectedCategory: {
|
|
type: Object,
|
|
default: function() {
|
|
return {}
|
|
}
|
|
},
|
|
arcId: {
|
|
type: String,
|
|
default: function() {
|
|
return ''
|
|
}
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
btnLoading: false,
|
|
uploadBigVisible: false,
|
|
skip: false,
|
|
options: {
|
|
target: '/api/collect/upload',
|
|
// 开启服务端分片校验功能
|
|
testChunks: true, // 是否分片
|
|
singleFile: true, // 单文件上传
|
|
uploadMethod: 'post', // 真正上传的时候使用的 HTTP 方法,默认 POST
|
|
allowDuplicateUploads: false, // 上传过得文件不可以再上传
|
|
parseTimeRemaining: function(timeRemaining, parsedTimeRemaining) {
|
|
return parsedTimeRemaining
|
|
.replace(/\syears?/, '年')
|
|
.replace(/\days?/, '天')
|
|
.replace(/\shours?/, '小时')
|
|
.replace(/\sminutes?/, '分钟')
|
|
.replace(/\sseconds?/, '秒')
|
|
},
|
|
// 服务器分片校验函数
|
|
checkChunkUploadedByResponse: (chunk, message) => {
|
|
const result = JSON.parse(message)
|
|
if (result.data.skipUpload) {
|
|
this.skip = true
|
|
return true
|
|
}
|
|
return (result.data.uploaded || []).indexOf(chunk.offset + 1) >= 0
|
|
},
|
|
headers: {
|
|
'Authorization': getToken()
|
|
}
|
|
},
|
|
attrs: {
|
|
accept: 'image/*'
|
|
},
|
|
// 修改上传状态
|
|
statusText: {
|
|
success: '上传成功',
|
|
error: '上传出错了',
|
|
uploading: '上传中...',
|
|
paused: '暂停中...',
|
|
waiting: '等待中...',
|
|
cmd5: '计算文件MD5中...'
|
|
},
|
|
fileList: [],
|
|
nowDate: null,
|
|
submitted: false
|
|
}
|
|
},
|
|
computed: {
|
|
...mapGetters([
|
|
'baseApi'
|
|
])
|
|
},
|
|
methods: {
|
|
fileSuccess(rootFile, file, response, chunk) {
|
|
this.chunkOffset = []
|
|
const result = JSON.parse(response)
|
|
console.log('result', result)
|
|
// 是否需要合并
|
|
// if (result.data.needMerge && !this.skip) {
|
|
// } else {
|
|
// console.log('上传成功,不需要合并')
|
|
// }
|
|
this.fileList.push(file)
|
|
if (result.code === 200 && this.fileList.length !== 0) {
|
|
this.submitted = true
|
|
} else {
|
|
this.submitted = false
|
|
}
|
|
if (this.skip) {
|
|
this.skip = false
|
|
}
|
|
},
|
|
filesRemove(file, index) {
|
|
this.fileList = []
|
|
const uploaderInstance = this.$refs.uploader.uploader
|
|
const temp = uploaderInstance.fileList.findIndex(e => e.uniqueIdentifier === file.uniqueIdentifier)
|
|
if (temp > -1) {
|
|
uploaderInstance.fileList[temp].cancel() // 这句代码是删除所选上传文件的关键
|
|
}
|
|
},
|
|
handleUploadConfirm() {
|
|
if (this.$refs.uploader.fileList.length === 0) {
|
|
this.$message.error('请选择要上传的文件!')
|
|
return false
|
|
}
|
|
this.nowDate = getCurrentTime()
|
|
this.$refs.uploader.fileList.map(async(item, index) => {
|
|
const json = {}
|
|
const jsonArray = []
|
|
const jsonString = {}
|
|
if (item.file.type.substring(0, item.file.type.indexOf('/')) === 'image') {
|
|
const fileBase64 = await this.getBase64(item)
|
|
const imgRes = await this.getImgPx(fileBase64)
|
|
item.file.px = imgRes.width + 'px*' + imgRes.height + 'px'
|
|
} else {
|
|
item.file.px = ''
|
|
}
|
|
jsonString.file_name = item.file.name
|
|
jsonString.file_size = item.file.size
|
|
jsonString.file_type = item.file.name.substring(item.name.lastIndexOf('.') + 1, item.file.name.length)
|
|
// jsonString.file_path = res.data.data
|
|
jsonString.file_path = ''
|
|
jsonString.sequence = null
|
|
jsonString.archive_id = this.arcId
|
|
jsonString.file_dpi = item.file.px
|
|
jsonString.file_thumbnail = ''
|
|
jsonString.create_time = this.nowDate
|
|
jsonString.id = null
|
|
jsonArray.push(jsonString)
|
|
|
|
json.categoryId = this.selectedCategory.id
|
|
json.archivesId = this.arcId
|
|
json.identifier = item.uniqueIdentifier
|
|
json.filename = item.name
|
|
// chunk.offset
|
|
json.totalChunks = item.chunks.length - 1
|
|
json.totalSize = item.size
|
|
json.fileJsonString = JSON.stringify(jsonArray)
|
|
|
|
if (item.completed && this.submitted) {
|
|
this.btnLoading = true
|
|
this.submitted = false
|
|
axios.post(this.baseApi + '/api/collect/merge', json, { headers: {
|
|
'Authorization': getToken()
|
|
}}).then((res) => {
|
|
console.log(res)
|
|
if (res.data.code === 200 && res.data.data !== '') {
|
|
this.$message.success('上传成功')
|
|
} else {
|
|
this.$message.error('上传失败')
|
|
}
|
|
this.$emit('close-dialog')
|
|
this.uploadBigVisible = false
|
|
this.fileList = []
|
|
this.btnLoading = false
|
|
this.$refs.uploader.files = []
|
|
this.$refs.uploader.fileList = []
|
|
this.$refs.uploader.uploader.fileList = []
|
|
this.$refs.uploader.uploader.files = []
|
|
})
|
|
} else {
|
|
this.submitted = false
|
|
this.$message.error('请耐心等待文件上传完成后再保存!')
|
|
}
|
|
})
|
|
},
|
|
onFileError(rootFile, file, message, chunk) {
|
|
this.$message.error('上传出错:' + message)
|
|
},
|
|
filesAdded(file, fileList, event) {
|
|
file.forEach((e) => {
|
|
this.fileList.push(e)
|
|
this.computeMD5(e)
|
|
})
|
|
},
|
|
computeMD5(file) {
|
|
const maxMessage = '上传文件大小不能超过 10GB!'
|
|
const maxSize = 10 * 1024 * 1024 * 1024
|
|
if (file && file.size > maxSize) {
|
|
this.$message.warning(maxMessage)
|
|
return false
|
|
}
|
|
const fileReader = new FileReader()
|
|
const time = new Date().getTime()
|
|
const blobSlice =
|
|
File.prototype.slice ||
|
|
File.prototype.mozSlice ||
|
|
File.prototype.webkitSlice
|
|
let currentChunk = 0
|
|
// 文件分片大小
|
|
const chunkSize = 10 * 1024 * 1024
|
|
const chunks = Math.ceil(file.size / chunkSize)
|
|
const spark = new SparkMD5.ArrayBuffer()
|
|
// 文件状态设为"计算MD5"
|
|
file.cmd5 = true
|
|
file.pause()
|
|
loadNext()
|
|
fileReader.onload = (e) => {
|
|
spark.append(e.target.result)
|
|
if (currentChunk < chunks) {
|
|
currentChunk++
|
|
loadNext()
|
|
// 实时展示MD5的计算进度
|
|
console.log(
|
|
`第${currentChunk}分片解析完成, 开始第${
|
|
currentChunk + 1
|
|
} / ${chunks}分片解析`
|
|
)
|
|
} else {
|
|
const md5 = spark.end()
|
|
console.log(
|
|
`MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${
|
|
file.size
|
|
} 用时:${new Date().getTime() - time} ms`
|
|
)
|
|
spark.destroy() // 释放缓存
|
|
file.uniqueIdentifier = md5 // 将文件md5赋值给文件唯一标识
|
|
file.cmd5 = false // 取消计算md5状态
|
|
file.resume() // 开始上传
|
|
}
|
|
}
|
|
fileReader.onerror = function() {
|
|
this.error(`文件${file.name}读取出错,请检查该文件`)
|
|
file.cancel()
|
|
}
|
|
function loadNext() {
|
|
const start = currentChunk * chunkSize
|
|
const end = start + chunkSize >= file.size ? file.size : start + chunkSize
|
|
fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end))
|
|
}
|
|
},
|
|
// 将上传的图片转为base64
|
|
getBase64(file) {
|
|
const reader = new FileReader()
|
|
reader.readAsDataURL(file)
|
|
return new Promise((resolve) => {
|
|
reader.onload = () => {
|
|
resolve(reader.result)
|
|
}
|
|
})
|
|
},
|
|
// 获取图片的分辨率
|
|
getImgPx(img) {
|
|
const image = new Image()
|
|
image.src = img
|
|
return new Promise((resolve) => {
|
|
image.onload = () => {
|
|
const width = image.width
|
|
const height = image.height
|
|
resolve({ width, height })
|
|
}
|
|
})
|
|
},
|
|
handleCloseDialog(done) {
|
|
this.uploadBigVisible = false
|
|
this.fileList = []
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.uploader-big{
|
|
width: 100%;
|
|
.uploader{
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
width: 100%;
|
|
text-align: center;
|
|
margin-bottom: 8px;
|
|
.uploader-drop{
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
text-align: center;
|
|
height: 180px;
|
|
// padding: 20px 0;
|
|
border: none;
|
|
.uploader-btn{
|
|
// width: 120px;
|
|
margin: 20px 0 20px 0;
|
|
padding: 0;
|
|
border: none;
|
|
i{
|
|
font-size: 32px;
|
|
color: #1F55EB;
|
|
}
|
|
&:hover{
|
|
background-color: transparent;
|
|
}
|
|
}
|
|
.el-upload__tip{
|
|
font-size: 12px;
|
|
color: #A6ADB6;
|
|
}
|
|
}
|
|
}
|
|
.upload-big-button{
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: center;
|
|
align-items: center;
|
|
margin: 10px 0 5px 0;
|
|
}
|
|
}
|
|
.uploader-file[status="success"] .uploader-file-remove {
|
|
display:block
|
|
}
|
|
</style>
|