分片上传
引用:
import upload from '/@/components/upload.vue'
<el-dialog v-model="state.fileVisible" :title="t('page.parts.title')" :close-on-click-modal="false">
<upload :key="new Date().getTime()" :type="'file'" :fail="state.fail" :file-show="false" :limit="1" @closeFile="closeFile" />
</el-dialog>
封装文件upload.vue
<template>
<div>
<el-upload :accept="state.accept" v-show="state.fileType!='image'&&state.fileType!='files'" drag :file-list="state.fileList" :on-change="onUpload" :auto-upload="false" ref="uploadfile" :limit="state.limit" :show-file-list="state.fileShow">
<h3>{{t('page.parts.file')}}</h3>
<p class="tips">{{state.fileName}}</p>
</el-upload>
<el-upload :on-remove="handleRemove" accept=".jpg,.png,.webp" v-show="state.fileType=='image'" v-model:file-list="state.fileList" list-type="picture-card" :on-change="onUpload" :auto-upload="false" ref="uploadfile" :limit="state.limit" :show-file-list="state.fileShow">
<el-icon>
<Plus />
</el-icon>
</el-upload>
<el-upload :on-remove="handleRemove" accept="" v-show="state.fileType=='files'" v-model:file-list="state.fileList" :on-change="onUpload" :auto-upload="false" ref="uploadfile" :limit="state.limit" :show-file-list="state.fileShow">
<el-button class="sm-primary" type="primary">{{t('page.maintenance.selectAttachment')}}<a v-if="state.progressFile>0">({{state.progressFile}}%)</a></el-button>
</el-upload>
<div class="progress" v-if="state.fileType!='image'&&state.fileType!='files'">
<el-progress v-show="state.isProgress" :text-inside="true" :stroke-width="18" :percentage="state.progressFile" status="success" />
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive, provide, ref, onMounted, defineEmits, watch, toRefs } from 'vue'
import { uploadFile, mergeFile, getFile } from '/@/api/index'
import { partmaterialImport } from '/@/api/parts/parts'
import { ElMessage, ElNotification } from 'element-plus'
import TP from '/@/utils/public'
import SparkMD5 from 'spark-md5'
import { Plus } from '@element-plus/icons-vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const state = reactive({
fileList: [] as any[],
progressFile: ref(0),
fileName: ref(t('page.parts.tips1')),
isProgress: ref(false),
fileType: ref('image'),
fileShow: ref(false),
limit: ref(1),
imgData: ref([]),
accept: '.xlsx,.xls',
})
const props = defineProps({
type: String,
fileShow: Boolean,
limit: Number,
images: Object,
old: Number,
accept: String,
fail: Number,
})
const { type, fileShow, limit, images, old, accept, fail } = toRefs(props)
state.fileType = type!.value || 'image'
state.fileShow = fileShow!.value
state.limit = limit!.value || 1
watch(
() => fail?.value,
(val) => {
uploadfile.value.clearFiles()
state.progressFile = 0
}
)
if (accept) {
state.accept = accept.value || ''
}
if (state.fileType == 'image') {
const img = images?.value || []
if (img.length > 0) {
let arr = []
for (const iterator of img) {
const obj = { name: 1, url: TP.baseUrl + '/file/thumbnail/' + iterator, data: iterator }
arr.push(obj)
}
state.fileList = arr
}
}
const handleRemove = (file: any, fileList: any) => {
state.progressFile = 0
let arr = []
for (const iterator of fileList) {
arr.push(iterator.data)
}
emits('getImg', arr)
if (state.fileType == 'files') {
emits('closeFile', arr)
}
}
const emits = defineEmits(['closeFile', 'getImg', 'closeFile1'])
const uploadfile = ref<any>(null)
//文件改变时的状态
const onUpload = async (File: any, fileList: any) => {
let t = File.raw.type.split('/')[1]
if (state.fileType != 'image') {
} else {
let accept = '.jpg,.png,.webp,.jpeg'
if (accept.indexOf(t) == -1) {
ElMessage({ type: 'error', message: '请选择jpg或者png图片' })
state.fileList.pop()
return false
}
}
const chunkSize = 500 * 1024 //分片大小
const file = File.raw // 文件
const fileSize = File.size // 文件大小
let chunkCount = Math.ceil(fileSize / chunkSize) // 分片数量
state.fileName = File.name
state.isProgress = true
if (chunkSize > fileSize) {
chunkCount = 1
}
const fileMd5: any = await getFileMd5(file, chunkCount, chunkSize) // 文件md5,给文件一个唯一标识,可以是整个文件的MD5也可以是分片的MD5,根据后台接口定义
const obj = { md5: fileMd5, fileName: File.name, totalChunks: chunkCount } //合并参数
for (let i = 0; i < chunkCount; i++) {
const start = i * chunkSize //分片开始
const end = Math.min(fileSize, start + chunkSize) // 分片结束
const _chunkFile = File.raw.slice(start, end) // 分片文件
const formdata = new FormData()
formdata.append('chunkNumber', (i + 1).toString())
formdata.append('file', _chunkFile)
formdata.append('fileName', File.name)
formdata.append('md5', fileMd5)
await uploadChunkFile(formdata, i, obj)
}
}
//上传分片
const uploadChunkFile = async (formdata: any, i: number, obj: any) => {
var res = await uploadFile(formdata)
let progress = parseFloat((((i + 1) / obj.totalChunks) * 100).toFixed(1)) //控制进度条
state.progressFile = progress
if (i + 1 == obj.totalChunks) {
MergeFiles(obj) //上传完成,进行合并
}
}
//合并文件
const MergeFiles = (obj: object) => {
mergeFile(obj).then((res: any) => {
if (res.status == 0) {
if (state.fileType == 'image') {
state.imgData.push(res.data)
if (state.fileList.length > 0) {
for (const iterator of state.fileList) {
if (iterator.data) {
state.imgData.push(iterator.data)
}
}
}
emits('getImg', state.imgData)
} else {
if (old?.value) {
setTimeout(() => {
partmaterialImport({ fileId: res.data }).then((res: any) => {
if (res.status == 0) {
emits('closeFile', res.data)
// emits('closeFile1', { fileId: res.data, fileName: obj.fileName })
state.progressFile = 0
state.isProgress = false
state.fileName = t('page.parts.tips1')
uploadfile.value.clearFiles()
ElNotification({
title: '提示',
message: '导入成功',
type: 'success',
})
} else {
uploadfile.value.clearFiles()
state.progressFile = 0
state.isProgress = false
state.fileName = t('page.parts.tips1')
}
})
//重置
}, 1000)
} else {
emits('closeFile', res.data)
emits('closeFile1', { fileId: res.data, fileName: obj.fileName })
}
}
} else {
}
})
}
//MD5
const getFileMd5 = (file: File, chunkCount: number, chunkSize: number) => {
return new Promise((resolve, reject) => {
const blobSlice = File.prototype.slice
const chunks = chunkCount
let currentChunk = 0
const spark = new SparkMD5.ArrayBuffer()
const fileReader = new FileReader()
fileReader.onload = (e) => {
spark.append(e.target?.result)
currentChunk++
if (currentChunk < chunks) {
loadNext()
} else {
const md5 = spark.end()
resolve(md5)
}
}
fileReader.onerror = (e) => {
reject(e)
}
function loadNext() {
const start = currentChunk * chunkSize
let end = start + chunkSize
if (end > file.size) {
end = file.size
}
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
}
loadNext()
})
}
</script>
<style lang="scss" scoped>
.progress {
margin-top: 40px;
}
.tips {
color: #999;
margin-top: 6px;
}
:deep(.el-upload-list__item-actions:hover .el-upload-list__item-preview) {
display: none;
}
:deep(.el-upload-list--picture-card .el-upload-list__item-actions span + span) {
margin-left: 0px;
}
</style>