前端分片上传文件

首先说的是oss分片上传

要用oss分片上传首先要new一个oss对象,那项目里面先装上oss依赖具体怎么装我就不在写了,装好之后具体要new看代码

   const res = await POST_ALIOSS_GETSTS() //请求拿到oss相关的权限什么的下面好用
    ossInfo = res
    client = new OSS({
      // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
      region: `oss-${ossInfo.region}`,
      // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
      accessKeyId: ossInfo.credentials.access_key_id,
      accessKeySecret: ossInfo.credentials.access_key_secret,
      // 从STS服务获取的安全令牌(SecurityToken)。
      stsToken: ossInfo.credentials.security_token,
      // 填写Bucket名称,例如examplebucket。
      bucket: ossInfo.bucket,
    });

然后选择文件去上传

// 选择文件
const changeFile = (uploadFile: UploadFile) => {
  isUploadSuccessful.value = true
  percentageUpload.value = 0
  isUpload.value = true
  fileList.value = []
  fileList.value.push(uploadFile)
  // formData.value.zip_path = ''
  const fileFormData = new FormData()
  fileFormData.append("file", uploadFile.raw as Blob)
  const data = uploadFile.raw as Blob;
  const name = getbaseurl(data)
  formData.value.zip_path = name
  // const url = ossInfo.endpoint + '/' + name
  if (userInfo.oss_type == 'ali') {
    ossFragment(data, name)
  } else {
    // minio分片上传
    fileSharding(data, name, uploadRef.value)

  }

}

下面开始上传

const headers = {
  // 指定该Object被下载时的网页缓存行为。
  // "Cache-Control": "no-cache",
  // 指定该Object被下载时的名称。
  // "Content-Disposition": "example.txt",
  // 指定该Object被下载时的内容编码格式。
  // "Content-Encoding": "utf-8",
  // 指定过期时间,单位为毫秒。
  Expires: '43200000',
  // 指定Object的存储类型。
  // "x-oss-storage-class": "Standard",
  // 指定Object标签,可同时设置多个标签。
  // "x-oss-tagging": "Tag1=1&Tag2=2",
  // 指定初始化分片上传时是否覆盖同名Object。此处设置为true,表示禁止覆盖同名Object。
  // "x-oss-forbid-overwrite": "true",
};
// oss分片上传
const ossFragment = async (data, name) => {

  const options = {
    // 获取分片上传进度、断点和返回值。
    progress: (p, cpt, res) => {
      if (cpt) {
        abortCheckpoint = cpt
        console.log(res)
      }
      percentageUpload.value = Number((p * 100).toFixed(0))
      console.log(percentageUpload.value)
      if (percentageUpload.value == 100) {
        isUpload.value = false
      }
      // console.log("JD", Number((p * 100).toFixed(0)));
    },
    fileSize: data.size, // 文件大小
    // 设置并发上传的分片数量。
    parallel: 3,
    // 设置分片大小。默认值为1 MB,最小值为100 KB。
    partSize: 1024 * 1024 * 6,
    timeout: 43200000,//设置超时时间
    // uploadId: uploadId,
    headers,
    // 自定义元数据,通过HeadObject接口可以获取Object的元数据。
    // meta: { year: 2020, people: "test" },
    // mime: "text/plain",
  };
  try {
    // 分片上传。
    const res = await client.multipartUpload(name, data, {
      ...options,

    });
    console.log("---", res);
  } catch (err) {
    console.log("12121", err);
    isUploadSuccessful.value = false
  }
}

// 取消上传
const cancelUpload = () => {
  if (userInfo.oss_type == 'ali') {
    removeFile()
    isUpload.value = false
    client.abortMultipartUpload(
      abortCheckpoint.name,
      abortCheckpoint.uploadId
    );
  } else {
    cancelUploadMinio()
    // source.value.cancel('取消上传')
  }
  // 中断分片上传。

  removeFile()
  isUpload.value = false
}

里面有具体的取消上传获取上传进度的方法,还有上传过程中设置单片上传的过期时间,oss权限的时间过期时间需要在接口里面过去信息的时候去设置,具体应该是在服务端设置的

minio的分片上传

minio前端分片上传其实和oss差不多首先也要new一个minio对象

const getOssSigns = async () => {
    const res = await getOssSign()
    ossInfo = res

    if (res) {
        // console.log(res)
        createMinio({
            endPoint: res.endpoint,
            accessKey: res.access_key,
            secretKey: res.signature,
            port: res.policy,
            useSSL: false,
            sessionToken: res.security_token
        })
        return res
    }

}

const createMinio = (config: {
    endPoint: string
    accessKey: string
    secretKey: string
    port: string
    useSSL: boolean
    sessionToken: string
}) => {
    if (!config) throw new Error('minio配置信息错误')
    minioClient = new Minio.Minio.Client(config)
    // this.minioClient = minioClient
}



//上传操作 当你看到这一行的时候赶紧跑路这活干不下去 没法待
let index = 0;//上传顺序
let SuccessfulIndex = 0; //传成功顺序
// let tryTimes = 0; //重试次数最多三次
// let fileShardinglist = []
const chunkSize = 100 * 1024 * 1024; // 切片大小为5M
let sourceList: any = [] //上传成的CopySourceOptions链接数组
let objectsList: any = []//上传成功的链接
let dateTime: any = ''//当前时间戳
let minioClient: any = null //minio对象
let chunks//总的数量
let fileComponentEnd//图层库合并字段
let source: any = []//取消请求token
let sourceFolder //分片目标文件夹
const percentageUpload = ref<any>(0)
const isUploadSuccessful = ref<any>(true)
const isUpload = ref(false) // 上传弹窗的是否展示
const uploadRefs = ref<any>()
// 文件分片计算
const fileSharding = async (file, nameUrl, uploadRef) => {
    uploadRefs.value = uploadRef
    chunks = Math.ceil(file.size / chunkSize);
    dateTime = new Date().getTime()
    SuccessfulIndex = 0
    index = 0
    sourceList = []
    objectsList = []
    source = []
    percentageUpload.value = 0
    const res = await getOssSigns()
    if (res) {
        concurrence(file, nameUrl)
    }
}

// 并发请求
const concurrence = (file, nameUrl) => {
    let num: any = 3
    source = []
    if ((chunks - index) < 3) {
        num = chunks - index
    } else {
        num = 3
    }
    // console.log(index, num);
    for (let i = 0; i < num; i++) {
        index++
        // console.log(index, i, num);
        minioUploadApi(file, nameUrl, index)
    }
}


// 上传分片
const minioUploadApi = async (file, nameUrl, num) => {
    const slice = file.slice((num - 1) * chunkSize, num * chunkSize);
    const fileName = 'modelBasefragment/' + dateTime + '/'; // 文件名字
    sourceFolder = fileName
    const UploadFileFun = new UploadFile({
        ...ossInfo,
        fileData: slice,
        filePath: fileName,
        fileName: (num - 1),
        fileType: file.type
    })
    UploadFileFun.start()
        .then((res) => {
            // console.log('+++', res)
            if (res.status === 200) {
                SuccessfulIndex++
                // console.log('+++', slice, url, index, file.type)
                const data = new Minio.Minio.CopySourceOptions({
                    Bucket: ossInfo.bucket,
                    Object: fileName + (SuccessfulIndex - 1),
                })
                sourceList.push(data)
                objectsList.push(fileName + (SuccessfulIndex - 1))
                percentageUpload.value = Math.round((100 * SuccessfulIndex) / chunks)
                // console.log('+++', progress, data)
                // console.log(SuccessfulIndex, chunks);

                if (SuccessfulIndex < chunks) {
                    // console.log(SuccessfulIndex, chunks);
                    if (SuccessfulIndex % 3 == 0) {
                        // console.log(SuccessfulIndex % 3);
                        concurrence(file, nameUrl)
                    }
                } else if (SuccessfulIndex == chunks) {
                    // composePromise(nameUrl)
                    composePromise(fileName, nameUrl)
                }
                // 成功回调
            } else {
                // 失败回调
                console.log('上传失败!')
                isUploadSuccessful.value = false
            }
        })
        .catch((error) => {
            console.log('上传失败!', error)
            isUploadSuccessful.value = false

        })
    source.push(UploadFileFun.setCancelToken())
}


// 合并分片
const composePromise = async (composeUrl, nameUrl) => {
    console.log(composeUrl, nameUrl);

    const res = await GET_ALIOSS_COMPONENT({ isDelete: true, sourceFolder: composeUrl, target: nameUrl })
    console.log(res)
    if (res) {
        console.log('开始合并!')
    } else {
        isUpload.value = false
        uploadRefs.value?.clearFiles()

    }
}



// // 合并分片
// const composePromise = (nameUrl) => {
//     const destOption = new Minio.Minio.CopyDestinationOptions({
//         Bucket: ossInfo.bucket,
//         Object: nameUrl,
//     })
//     console.log(destOption, sourceList)
//     const composePromise = minioClient.composeObject(destOption, sourceList)
//     composePromise
//         .then((result) => {
//             console.log('Success...', result)
//             isUpload.value = false
//             removerobj()
//         })
//         .catch((e) => {
//             console.log('error', e)
//         })
// }

//删除分片
const removerobj = () => {
    console.log(objectsList);
    // if (objectsList.length > 0) {
    minioClient.removeObjects(ossInfo.bucket, objectsList, function (e) {
        if (e) {
            return console.log(e)
        }
        console.log('Success')
    })
    // }

}

//取消上传
const cancelUploadMinio = () => {
    if (source.length > 0 && isUpload.value) {
        source.forEach(res => {
            res.cancel('取消上传')
        })
        removerobj()
        uploadRefs.value?.clearFiles()
    }
}

上面其中用到的minio的封装上传在下面

const Minio = window['minio-js']
const userStore = useUserStore()

// 获取上传通信证
export async function getOssSign() {
	let oss_type = 'ali'
	const userInfo = userStore.$state.userInfo
	oss_type = userInfo.oss_type || 'ali'
	if (oss_type === 'ali') {
		const res = await POST_ALIOSS_GETSIGNATURE()
		return res
	} else {
		const res = await POST_ALIOSS_GETSTS()
		if (res) {
			const {
				bucket,
				endpoint,
				credentials: { access_key_id, access_key_secret, security_token }
			} = res
			const ipStartIdx = endpoint.indexOf('//')
			const ipEndIdx = endpoint.lastIndexOf(':')
			const ips = endpoint.substring(ipStartIdx + 2, ipEndIdx)
			const por = endpoint.substring(ipEndIdx + 1)
			return {
				bucket,
				endpoint: ips,
				policy: Number(por) || 80,
				access_key: access_key_id,
				signature: access_key_secret,
				security_token
			}
		}
	}
}

export class UploadFile {
	fileData: File
	filePath: string
	fileName: string
	fileType: string
	access_key: string
	signature: string
	policy: string
	bucket: string
	endpoint: string
	security_token: string
	hostUrl: string
	formData: FormData | null
	minioClient: Minio.Minio.Client | null
	fileBuffer: Buffer | null
	cancelToken: CancelTokenSource | null
	axiosMethod: string
	oss_type: string

	constructor(params: {
		fileData: File
		filePath: string
		fileName: string
		fileType?: string
		access_key: string
		signature: string
		policy: string
		bucket?: string
		endpoint?: string
		security_token: string
	}) {
		const {
			fileData,
			filePath,
			fileName,
			fileType = 'multipart/form-data',
			access_key,
			signature,
			policy,
			bucket = '',
			endpoint = '',
			security_token
		} = params
		this.fileData = fileData
		this.filePath = filePath
		this.fileName = fileName
		this.fileType = fileType
		this.hostUrl = ''
		this.access_key = access_key
		this.policy = policy
		this.signature = signature
		this.formData = null
		this.minioClient = null
		this.fileBuffer = null
		this.cancelToken = null
		this.axiosMethod = 'post'
		this.oss_type = 'ali'
		this.bucket = bucket || ''
		this.endpoint = endpoint || ''
		this.security_token = security_token
		this.init()
	}
	//------------------------------------------------初始化
	init() {
		const userInfo = userStore.$state.userInfo
		this.oss_type = userStore.$state.userInfo.oss_type || 'ali'
		this.hostUrl = userStore.$state.userInfo.oss_url
		const cancelToken = axios.CancelToken.source()
		this.cancelToken = cancelToken
		if (userInfo.oss_type === 'minio') {
			this.axiosMethod = 'put'
			this.createMinio({
				endPoint: this.endpoint,
				accessKey: this.access_key,
				secretKey: this.signature,
				port: this.policy,
				useSSL: false,
				sessionToken: this.security_token
			})
		} else {
			this.axiosMethod = 'post'
		}
	}

	/*******
	 * @description: 开始上传
	 * @param {*} progressCallback   上传进度callback
	 * @return {*}
	 */
	start(progressCallback: ((progressEvent: ProgressEvent) => void) | null = null): Promise<any> {
		return new Promise((resolve, reject) => {
			if (this.oss_type === 'ali') {
				this.getFormData(this.fileData, this.filePath, this.fileName)
				this.upload(progressCallback, this.formData)
					.then((res) => {
						resolve(res)
					})
					.catch((err) => {
						reject(err)
					})
			} else {
				this.getPutSignUrl(this.bucket, this.fileName, this.filePath, (error, presignedUrl) => {
					if (error) reject(error)
					this.hostUrl = presignedUrl
					this.upload(progressCallback, this.fileData)
						.then((res) => {
							resolve(res)
						})
						.catch((err) => {
							reject(err)
						})
				})
			}
		})
	}
	//--------------------------------------------------------------------------公网阿里云oss

	/*******
	 * @description: 整理formData
	 * @param {*} fileData
	 * @param {*} filePath
	 * @param {*} fileName
	 * @return {*}
	 */
	getFormData(fileData: File, filePath: string, fileName: string): FormData {
		const formData = new FormData()
		formData.append('key', `${filePath}${fileName}`) // 存储在oss的文件路径
		formData.append('OSSAccessKeyId', this.access_key) // accessKeyId
		formData.append('policy', this.policy) // policy
		formData.append('Signature', this.signature) // 签名
		// 如果是base64文件,那么直接把base64字符串转成blob对象进行上传就可以了
		formData.append('file', fileData)
		formData.append('success_action_status', '200') // 成功后返回的操作码
		this.formData = formData
		return formData
	}

	//-------------------------------------------------------------------------------------------------局域网minIO存储

	/*******
	 * @description: 创建minio客户端
	 * @param {*} config {endPoint:存储服务ip地址(xxx.xxx.xxx),port:端口,useSSL:是否使用ssl,accessKey:账号,secretKey:密码}
	 * @return {*}
	 */
	createMinio(config: {
		endPoint: string
		accessKey: string
		secretKey: string
		port: string
		useSSL: boolean
		sessionToken: string
	}) {
		if (!config) throw new Error('minio配置信息错误')
		const minioClient = new Minio.Minio.Client(config)
		this.minioClient = minioClient
	}

	/*******
	 * @description: 获取文件二进制流
	 * @param {*} fileData 文件数据
	 * @param {*} callBack 转换完成回调
	 * @return {*}
	 */
	getFileBuffer(fileData: File, callBack: (buffer: Buffer) => void) {
		const reader = new FileReader()
		reader.readAsArrayBuffer(fileData)
		reader.onload = (e) => {
			const res = e.target?.result as ArrayBuffer
			const buffer = Buffer.from(res)
			this.fileBuffer = buffer
			callBack(buffer)
		}
	}

	/*******
	 * @description: 获取签名后得上传地址
	 * @param {*} bucket String  私有化存储桶名称
	 * @param {*} fileName String 文件名称
	 * @param {*} filePath String 文件存储路径
	 * @param {*} validityTime number 上传链接有效期(秒,默认设置1小时)
	 * @return {*}
	 */
	getPutSignUrl(
		bucket: string,
		fileName: string,
		filePath: string,
		callBack: (error: any, presignedUrl: string) => void,
		validityTime: number = 60 * 60 * 24
	) {
		this.minioClient?.bucketExists(bucket, (err) => {
			if (err) throw new Error('minIO存储桶不存在')
			this.minioClient?.presignedPutObject(
				bucket,
				`${filePath}${fileName}`,
				validityTime,
				(err, presignedUrl) => {
					this.hostUrl = presignedUrl
					callBack(err, presignedUrl)
				}
			)
		})
		return this.hostUrl
	}

	// --------------------------------------------------------上传
	/*******
	 * @description: 上传
	 * @param {*} progressCallback  上传进度callback
	 * @return {*}
	 */
	upload(
		progressCallback: ((progressEvent: ProgressEvent) => void) | null = null,
		data: FormData | Buffer | null = this.formData || this.fileBuffer
	): Promise<any> | undefined {
		if (!data) return

		const config: AxiosRequestConfig = {
			url: this.hostUrl,
			method: this.axiosMethod,
			data,
			withCredentials: false,
			headers: {
				'Content-Type': this.oss_type === 'ali' ? 'multipart/form-data' : this.fileType
			},
			// 监听上传进度
			onUploadProgress: progressCallback,
			// 取消上传方法
			cancelToken: this.cancelToken?.token
		}

		return axios(config)
	}

	cancelUpload() {
		this.cancelToken?.cancel()
		this.cancelToken = null
	}

	setCancelToken() {
		if (this.cancelToken) {
			return this.cancelToken
		}
	}
}

具体使用还带看个人研究结合个人需求实际

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
前端vue大文件分片是一种将大文件切分成多个块进行上的技术,通过将文件分成若干小块,逐个上,可以减少重新上的开销,并提高上的效率。这种技术可以应对网络不稳定、输中断等问题,当网络输中断时,只需要重新输剩余的分片,而不需要重新上整个文件,从而节省了输时间和成本。为了实现断点续功能,需要与后台接口配合,后台接口负责接收并保存已上文件块,以便在后续上时跳过已上的部分。对于前端vue大文件分片,需要后台接口的支持,后台接口需要接收分片并正确组合,这样在上中断时只需要找到中断的分片,而不用重新上全部内容,进一步节约成本。具体的实现方案可以根据具体需求和后台接口的支持来进行选择和开发。123 #### 引用[.reference_title] - *1* *2* [Vue实现大文件分片,包括断点续以及上进度条](https://blog.csdn.net/yjxkq99/article/details/128942133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *3* [前端vue实现大文件分片](https://blog.csdn.net/tt18473481961/article/details/128819751)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值