|
|
<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>
|