分片上传

引用:

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>

posted @ 2024-01-04 17:45  FancyAnnaYL  阅读(12)  评论(0编辑  收藏  举报