Minio文件分片上传并获取上传进度

直接上干货
minio 的上传逻辑是,文件大于5M,执行分片上传. 但是没有对外抛出相关的能力,在8.0版本后允许通过特殊途径执行

在这里插入图片描述
在这里插入图片描述

核心逻辑

前端分片并获取MD5
初始化分片信息
返回每个分片的地址和id
前端上传
合并文件

后端代码

我也是抄的这个博客,写的很完整了,改改好用

前端代码

import {init, mergeMultipartUpload, upload} from "@/net/api";
import SparkMD5 from 'spark-md5'

const CHUNK_SIZE = 5 * 1024 * 1024

export let files = []

const addFiles = async (f, roomId, cb) => {
    for (let file of f) {
        const data = {
            // 文件名
            name: file.name + '.' + file.extension,
            // 文件类型
            type: file.type,
            // 文件md5
            md5: '',
            // 切片列表
            chunks: [],
            // 上传进度
            percentage: 0,
            // 已上传大小
            uploaded: '',
            // 开始上传时间
            uploadTime: 0,
            // 上传速度
            speed: 0,
            // 切片完成度
            chunkedProgress: 0,
            // 总数量大小
            fileSizeByte: file.size,
            // 总大小
            fileSize: formatSize(file.size),
            // 原始文件
            file: file,
            // url
            url: '',
        }

        await inputChange(data, (list, len) => {
            const listLen = list.length
            data.chunkedProgress = listLen <= 0 ? 0 : Math.round((listLen / len) * 10000) / 100.0
        })
        console.log('切片完成:', data.md5)

        const response = await init({
            filename: file.name + '.' + file.extension,
            partCount: data.chunks.length,
            md5: data.md5
        })
        const uploadUrls = response.uploadUrls
        const promises = [];
        data.uploadTime = new Date().getTime()
        console.log('开始上传:', data.uploadTime)
        for (let i = 0; i < uploadUrls.length; i++) {
            promises.push(new Promise((resolve) => {
                upload(uploadUrls[i], data.chunks[i].file, (loaded, total) => {
                    data.chunks[i].uploadProgress = loaded
                    data.chunks[i].total = total
                }).then(() => {
                    resolve()
                })
            }))
        }
        const t = setInterval(() => {
            countSpeed(data)
        }, 300)
        Promise.all(promises).then(async () => {
            files.push(data)
            clearInterval(t)
            if (response.uploadId) {
                await mergeMultipartUpload({
                    uploadId: response.uploadId,
                    objectName: response.objectName
                })
                console.log('文件合并完成!')
            }
            data.url = response.objectName
            if (files.length === f.length) {
                files = []
                cb({...data.file, url: data.url}, true)
            } else {
                cb({...data.file, url: data.url}, false)
            }
        })
    }

}

const countSpeed = (data) => {
    let uploaded = 0;
    data.chunks.forEach(x => {
        uploaded += x.uploadProgress
    })
    const useTime = new Date().getTime() - data.uploadTime
    const speed = uploaded / (useTime / 1000)
    data.percentage = uploaded <= 0 ? 0 : Math.round((uploaded / data.fileSizeByte) * 10000) / 100.0
    data.uploaded = formatSize(uploaded)
    data.speed = formatSize(speed)
    console.log('总大小:', data.fileSize, ' 上传速度:', data.speed, ' 上传百分比:', data.percentage, ' 已上传:', data.uploaded)
}

const inputChange = async (file, cb) => {
    if (file) {
        const fileChunkList = await createFileChunk(file.file, (data, size) => {
            cb(data, size)
        })
        file.md5 = await getFileChunkMd5(fileChunkList)
        file.chunks = fileChunkList
    }
}

const createFileChunk = async (file, cb) => {
    const list = []
    const type = file.type
    let size = 0
    const len = Math.ceil(file.size / CHUNK_SIZE)
    while (size < file.size) {
        const data = {
            file: file.blob.slice(size, size + CHUNK_SIZE),
            type: type,
            uploadProgress: 0,
            total: 0
        }
        list.push(data)
        size += CHUNK_SIZE
        cb(list, len)
    }
    return list;
}

const getFileChunkMd5 = (fileChunkList) => {
    return new Promise((resolve) => {
        // 总切片数
        const chunkSize = fileChunkList.length
        // 当前处理位置
        let currentChunk = 0
        // SparkMD5实例的ArrayBuffer
        const spark = new SparkMD5.ArrayBuffer()

        const fileReader = new FileReader()
        fileReader.onload = (e) => {
            try {
                spark.append(e.target.result)
            } catch (error) {
                console.log('获取Md5错误,错误位置:' + currentChunk)
            }
            currentChunk++

            if (currentChunk < chunkSize) {
                loadNext()
            } else {
                resolve(spark.end())
            }
        }
        fileReader.onerror = function () {
            console.warn('Md5:文件读取错误')
        }

        function loadNext() {
            fileReader.readAsArrayBuffer(fileChunkList[currentChunk].file)
        }

        loadNext()
    })
}

const formatSize = (size) => {
    if (size < 1024) {
        return size.toFixed(0) + ' bytes'
    } else if (size < 1024 * 1024) {
        return (size / 1024.0).toFixed(0) + ' KB'
    } else if (size < 1024 * 1024 * 1024) {
        return (size / 1024.0 / 1024.0).toFixed(1) + ' MB'
    } else {
        return (size / 1024.0 / 1024.0 / 1024.0).toFixed(1) + ' GB'
    }
}

export {
    addFiles
}

请求代码

import request from "@/utils/request";

export function init(data) {
  return request({
    url: '/multipart/init',
    method: 'post',
    data: data
  })
}

export function upload(url, data,cb) {
  return request({
    url: url,
    method: 'put',
    onUploadProgress: (event) => {
      // console.log(event,'upload')
      cb(event.loaded, event.total)
    },
    headers: {
      'Content-Type': 'application/octet-stream'
    },
    data: data
  })
}

export function mergeMultipartUpload(data) {
  return request({
    url: '/multipart/complete',
    method: 'put',
    data: data
  })
}

使用实例, 可以传入多个文件, roomid是我业务的编号, 每个文件传完都会回调当前文件和是否上传完成,改改好用

        await addFiles(files, roomId, (file, isOver) => {
          sendFileMessage({
            name: file.name + '.' + file.extension,
            size: file.size,
            type: file.type,
            url: file.url,
          }, roomId, isOver)
        })

推荐一个作者开源IM
信使 核心代码在这里可以看到

在这里插入图片描述

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值