Jelajahi Sumber

上传组件植入

于添 7 bulan lalu
induk
melakukan
11457a5df2

+ 283 - 49
src/components/UpLoadBreakPoint/index.vue

@@ -16,24 +16,44 @@
 			</div>
 		</a-upload-dragger>
 
-		<div style="margin-bottom: 20px">
+		<!-- <div style="margin-bottom: 20px">
 			<a-button v-if="uploadFileList.length > 0" type="primary" @click="uploadFilesList">上传</a-button>
-		</div>
+		</div> -->
 
 		<div v-for="(item, index) in uploadFileList" :key="index">
 			<div style="padding: 10px">
 				<div style="display: flex; width: 100%; align-items: center; justify-content: space-between">
 					<div>
-						<span>{{ item.name }}</span>
+						<span>{{ item.name.length > 20 ? item.name.slice(0, 20) + '...' + item.fileSuffix : item.name }}</span>
 					</div>
 					<div>
 						<span v-if="item.time != ''" style="display: block; color: blue">{{ item.time }}</span>
-						<span
-							v-if="item.percents == 0 || item.percents == 100"
-							style="color: red; cursor: pointer; margin-left: 10px"
-							@click="handlerRemoveItem(index)"
-							>删除</span
-						>
+					</div>
+					<div>
+						<div>
+							<span
+								v-if="item.percents == 0 || item.percents == 100"
+								style="color: red; cursor: pointer; margin-left: 10px"
+								@click="handlerRemoveItem(index)"
+								>删除</span
+							>
+							<span
+								v-if="
+									item.percents >= 0 &&
+									item.percents < 100 &&
+									(pauseFlags[item.md5] == false || pauseFlags[item.md5] == undefined)
+								"
+								style="color: blue; cursor: pointer; margin-left: 10px"
+								@click="pauseUpload(index)"
+								>暂停</span
+							>
+							<span
+								v-if="item.percents >= 0 && item.percents < 100 && pauseFlags[item.md5] == true"
+								style="color: green; cursor: pointer; margin-left: 10px"
+								@click="resumeUpload(index)"
+								>恢复</span
+							>
+						</div>
 					</div>
 				</div>
 
@@ -66,6 +86,9 @@
 	import SparkMD5 from 'spark-md5'
 	import tool from '@/utils/tool'
 	import { message } from 'ant-design-vue'
+
+	const pauseFlags = ref({}) // 控制每个文件是否暂停 { md5: true/false }
+	const uploadingTasks = ref({}) // 正在上传的任务 { md5: true }
 	//当前选中的文件
 	const currentFile = ref(null)
 	const chunkSize = ref(5 * 1024 * 1024)
@@ -98,10 +121,36 @@
 	const startTime = ref(0) // 开始时间戳(毫秒
 	const totalSize = ref(0) // 开始时间戳(毫秒
 	const handlerRemoveItem = (index) => {
+		const item = uploadFileList.value[index]
+		if (item && item.md5) {
+			delete pauseFlags.value[item.md5] // 清理暂停标志
+		}
 		uploadFileList.value.splice(index, 1)
 		emit('onSuccess', uploadFileList.value)
 	}
+	const pauseUpload = (index) => {
+		const item = uploadFileList.value[index]
+		if (item && item.md5) {
+			pauseFlags.value[item.md5] = true
+			uploadingTasks.value[item.md5] = false
+		}
+	}
+
+	const resumeUpload = (index) => {
+		const item = uploadFileList.value[index]
+		if (!item || !item.md5) return
 
+		pauseFlags.value[item.md5] = false
+
+		// 如果当前上传任务小于 2,则开始上传
+		const activeTasks = Object.keys(uploadingTasks.value).filter((key) => uploadingTasks.value[key])
+		if (activeTasks.length < 2) {
+			uploadingTasks.value[item.md5] = true
+			// uploadSingleFile(item)
+		} else {
+			pauseFlags.value[item.md5] = true
+		}
+	}
 	const calculateFileMD5 = (file) => {
 		return new Promise((resolve, reject) => {
 			const reader = new FileReader()
@@ -124,19 +173,19 @@
 		const percent = Math.round((uploadedSize.value / totalSize.value) * 100)
 		console.log(`上传进度: ${percent}%`)
 	}
-	const calculateSpeed = () => {
+	const calculateSpeed = (startTime, uploadedSize) => {
 		const currentTime = new Date().getTime()
-		const timeElapsed = (currentTime - startTime.value) / 1000 // 单位:秒
+		const timeElapsed = (currentTime - startTime) / 1000 // 单位:秒
 		if (timeElapsed > 0) {
-			const speed = uploadedSize.value / timeElapsed // 单位:字节/秒
+			const speed = uploadedSize / timeElapsed // 单位:字节/秒
 			return speed
 		}
 		return 0
 	}
-	const estimateRemainingTime = () => {
-		console.log('疑问', ' 总的 ', totalSize.value, ' 变化的 ', uploadedSize.value)
-		const remainingSize = totalSize.value - uploadedSize.value // 剩余文件大小
-		const speed = calculateSpeed() // 平均上传速度(字节/秒)
+	const estimateRemainingTime = (startTime, uploadedSize, totalSize) => {
+		console.log('疑问', ' 总的 ', totalSize, ' 变化的 ', uploadedSize)
+		const remainingSize = totalSize - uploadedSize // 剩余文件大小
+		const speed = calculateSpeed(startTime, uploadedSize) // 平均上传速度(字节/秒)
 
 		if (speed > 0) {
 			const remainingTimeSeconds = remainingSize / speed // 剩余时间(秒)
@@ -154,8 +203,6 @@
 	}
 	// 异步方法:选择文件时触发 ============
 	const handleFileChange = async (file) => {
-		fileMd5.value = ''
-		successfulChunkPercents.value = 0
 		// const reader = new FileReader()
 		//   const spark = new SparkMD5.ArrayBuffer()
 		// console.log("读取文件",file.raw)
@@ -175,17 +222,11 @@
 		// })
 		// console.log('111文件的md5哈希值是 fileContent:', fileContent)
 		// 计算MD5哈希值
-		fileMd5.value = await calculateFileMD5(file.raw)
-		// fileMd5.value = md5(fileContent)
-		console.log('111文件的md5哈希值是:', fileMd5.value)
-
-		console.log('file对象的大小:', file.size)
-		console.log('file对象:', file)
-		chunkCount.value = Math.ceil(file.size / chunkSize.value) // 还是计算分块数量,向上取整
-		chunksUploaded.value = 0 // 已上传的分片数量
-		fileSuffix.value = '.' + file.raw.name.split('.').pop() // 得到.文件类型
-		successfulChunkPercents.value = 0 // 上传成功的分片百分比
-		currentFile.value = await new Promise((resolve, reject) => {
+		let fileMd5 = await calculateFileMD5(file.raw)
+		file.raw.md5 = fileMd5
+		console.log('====开始获取File对象了...', file)
+		let fileSuffix = '.' + file.raw.name.split('.').pop() // 得到.文件类型
+		let currentFile = await new Promise((resolve, reject) => {
 			console.log('====开始获取File对象了...')
 			resolve(file.raw)
 			reject('获取File对象失败')
@@ -201,24 +242,34 @@
 		//     )
 		//     chunkList.value[i] = currentFile.value.slice(start, end)
 		// }
-		splitFileByChunkSize(currentFile.value, chunkSize.value)
-
-		console.log('md5:', fileMd5.value)
-		console.log('chunkList:', chunkList.value)
-		console.log('fileSuffix:', fileSuffix.value)
-		console.log('currentFile:', currentFile.value)
-		uploadFileList.value.push({
+		let chunkList = splitFileByChunkSize(currentFile, chunkSize.value)
+		console.log('file对象:', file)
+		return {
 			name: file.raw.name,
-			size: currentFile.value.size,
-			md5: fileMd5.value, // md5哈希值
-			chunks: chunkList.value, // 分块列表
-			fileSuffix: fileSuffix.value, // 后缀
+			size: currentFile.size,
+			md5: fileMd5, // md5哈希值
+			chunks: chunkList, // 分块列表
+			fileSuffix: fileSuffix, // 后缀
 			percents: 0,
 			time: ''
-		}) // 使用对象数组进行处理 ============
-		chunkList.value = []
-		console.log('uploadFileList是:', uploadFileList.value)
-		console.log('结束handleFileChange了')
+		}
+
+		// console.log('md5:', fileMd5.value)
+		// console.log('chunkList:', chunkList.value)
+		// console.log('fileSuffix:', fileSuffix.value)
+		// console.log('currentFile:', currentFile.value)
+		// uploadFileList.value.push({
+		// 	name: file.raw.name,
+		// 	size: currentFile.value.size,
+		// 	md5: fileMd5.value, // md5哈希值
+		// 	chunks: chunkList.value, // 分块列表
+		// 	fileSuffix: fileSuffix.value, // 后缀
+		// 	percents: 0,
+		// 	time: ''
+		// }) // 使用对象数组进行处理 ============
+		// chunkList.value = []
+		// console.log('uploadFileList是:', uploadFileList.value)
+		// console.log('结束handleFileChange了')
 	}
 
 	const splitFileByChunkSize = (file, chunkSizeValue) => {
@@ -233,6 +284,7 @@
 		//     ' 单块 ',
 		//     chunkSizeValue
 		// )
+		let chunkList = []
 		for (let i = 0; i < chunkCount; i++) {
 			const start = i * chunkSizeValue
 			const min = start + chunkSizeValue
@@ -252,7 +304,7 @@
 			//     ' ssss ',
 			//     fileSize
 			// )
-			chunkList.value[i] = file.slice(start, end) // 注意:slice 是 [start, end) 前闭后开区间
+			chunkList[i] = file.slice(start, end) // 注意:slice 是 [start, end) 前闭后开区间
 
 			console.log(
 				'准备开始:',
@@ -269,7 +321,57 @@
 			)
 		}
 
-		console.log('分片完成:', chunkList.value)
+		console.log('分片完成:', chunkList)
+		return chunkList
+	}
+
+	const checkMd5List = async (uploadFile) => {
+		let md5List = []
+		let element = {
+			md5: uploadFile.md5,
+			chunkSize: uploadFile.chunks.length,
+			fileName: uploadFile.name,
+			fileSuffix: uploadFile.fileSuffix
+		}
+		md5List.push(element)
+
+		await axios
+			.post('/api/webapp/minio/checkMd5List', md5List, { headers: { Token: tool.data.get('TOKEN') } })
+			.then((res) => {
+				console.log('文件上传返回结果:', res.data)
+				// return
+				var list = res.data
+				if (list.length !== 0) {
+					let upList = []
+					for (let item of list) {
+						console.log('item回来的', JSON.stringify(item))
+						if (uploadFile.md5 === item.md5) {
+							uploadFile.userFileId = item.userFileId
+							//重要的步骤
+							if (item.userFileId) {
+								uploadFile.percents = 100
+								pauseFlags.value[item.md5] = false
+								// emit('onSuccess', uploadFile)
+							}
+							// upList.push(item)
+						}
+					}
+					console.log('upList是:', upList)
+					// uploadFileList.value.push(uploadFile)
+					// emit('onSuccess', uploadFileList.value)
+				} else {
+					// clearFileList()
+				}
+
+				// 文件均存在minio中了,无需上传
+				// if (uploadFileList.value.length === 0) {
+				// 	successfulChunkPercents.value = 100
+				// 	alert('文件上传成功')
+				// }
+			})
+			.catch((error) => {
+				console.log('检查返回错误', error)
+			})
 	}
 
 	// 点击上传按钮触发多文件上传 ===============
@@ -364,6 +466,13 @@
 					console.log('上传第', i + 1, '个分片')
 					let chunk = item.chunks[i]
 					console.log('去上传...', i + 1, chunk)
+
+					// 检查是否暂停
+					console.log('看看...', ' 列表 ', pauseFlags.value, ' md5 ', md5)
+
+					while (pauseFlags.value[md5] && true == pauseFlags.value[md5]) {
+						await new Promise((resolve) => setTimeout(resolve, 100)) // 等待 1 秒后再次检查
+					}
 					chunkPromises.push(
 						await uploadFilesChunk(
 							{
@@ -431,9 +540,11 @@
 		}
 		console.log('合并结果2:', uploadFileList.value)
 		upLoadTag.value = false
+		// 上传完成,清理任务状态
+		delete uploadingTasks.value[md5]
 		emit('onUpLoading', upLoadTag.value)
 		let finalRes = true
-		emit('onSuccess', uploadFileList.value)
+		// emit('onSuccess', uploadFileList.value)
 		// for (const result of mergeResults) {
 		// 	if (result.data === '失败') {
 		// 		finalRes = false
@@ -518,8 +629,29 @@
 	const downloadFile = (url) => {
 		window.location.href = url
 	}
-	const beforeUpload = (file) => {
-		handleFileChange({ raw: file })
+	const beforeUpload = async (file) => {
+		console.log('选择了文件', file)
+		let upFile = await handleFileChange({ raw: file })
+		console.log('可以上传的文件内容是', upFile)
+		// 检查本地 uploadFileList 是否已存在该 md5 文件
+		const exists = uploadFileList.value.some((item) => item.md5 === upFile.md5)
+		if (exists) {
+			message.warning('该文件已存在,不再重复添加')
+			return false
+		}
+		await checkMd5List(upFile)
+		uploadFileList.value.push(upFile)
+		emit('onSuccess', uploadFileList.value)
+		const activeTasks = Object.keys(uploadingTasks.value).filter((key) => uploadingTasks.value[key])
+
+		if (activeTasks.length < 2) {
+			pauseFlags.value[upFile.md5] = false
+			uploadingTasks.value[upFile.md5] = true
+		} else {
+			pauseFlags.value[upFile.md5] = true
+			message.warning('上传队列已满,将进入暂停状态')
+		}
+		await uploadSingleFile(upFile)
 		return false // 阻止默认上传
 	}
 	const handleChange = (info) => {
@@ -530,6 +662,108 @@
 		}
 	}
 
+	const uploadSingleFile = async (fileObj) => {
+		const file = fileObj
+		const md5 = file.md5
+		const index = uploadFileList.value.findIndex((item) => item.md5 === md5)
+
+		if (index === -1) return
+
+		const item = uploadFileList.value[index]
+
+		if (!item || item.userFileId) return
+
+		// // 如果是暂停状态则不执行上传
+		// while (pauseFlags.value[md5]) {
+		// 	await new Promise((resolve) => setTimeout(resolve, 500))
+		// }
+		// 添加到正在上传任务中
+		uploadingTasks.value[md5] = true
+		file.startTime = new Date().getTime()
+		file.uploadedSize = 0
+
+		const chunkPromises = []
+
+		for (let i = 0; i < item.chunks.length; i++) {
+			let chunk = item.chunks[i]
+
+			while (pauseFlags.value[md5]) {
+				await new Promise((resolve) => setTimeout(resolve, 500))
+			}
+
+			chunkPromises.push(
+				await uploadFilesChunk(
+					{
+						md5,
+						chunk,
+						chunkIndex: i + 1,
+						fileSuffix: item.fileSuffix,
+						chunkSize: item.chunks.length,
+						fileName: item.name
+					},
+					() => {
+						// chunksUploaded.value++
+						// uploadChunks.value++
+
+						// successfulChunkPercents.value = 100 * (uploadChunks.value / allChunks.value).toFixed(2)
+						item.uploadedSize += chunk.size // 更新已上传大小
+						const remainingTime = estimateRemainingTime(item.startTime, item.uploadedSize, item.size)
+						console.log(`预计剩余时间: ${formatTime(remainingTime)}`)
+						// item.percents = (100 * (uploadChunks.value / item.chunks.length)).toFixed(2)
+						item.time = formatTime(remainingTime)
+
+						const percent = ((i + 1) / item.chunks.length) * 100
+						item.percents = percent.toFixed(2)
+						console.log(`我得名字: `, item.name, ' i ', i, ' item.chunks.length ', item.chunks.length)
+						// item.time = formatTime(estimateRemainingTime())
+					}
+				)
+			)
+		}
+
+		await Promise.all(chunkPromises)
+
+		// 合并分片
+		const mergeResult = await axios.post(
+			`/api/webapp/minio/merge?md5=${md5}&fileSuffix=${item.fileSuffix}&chunkTotal=${item.chunks.length}&fileName=${item.name}&fileSize=${item.size}`,
+			null,
+			{ headers: { Token: tool.data.get('TOKEN') } }
+		)
+
+		uploadFileList.value[index].userFileId = mergeResult.data.userFileId
+		uploadFileList.value[index].percents = 100
+
+		uploadingTasks.value[item.md5] = false
+		// item.time = '上传完成'
+		// 尝试恢复一个被暂停的任务
+		autoResumePausedUpload()
+		// upLoadTag.value = false
+		emit('onSuccess', uploadFileList.value)
+	}
+
+	// watch(
+	// 	() => uploadFileList.value,
+	// 	(newValue) => {
+	// 		emit('onSuccess', newValue)
+	// 	},
+	// 	{ immediate: true }
+	// )
+	const autoResumePausedUpload = async () => {
+		const activeTasks = Object.keys(uploadingTasks.value).filter((key) => uploadingTasks.value[key])
+		if (activeTasks.length >= 2) return
+
+		// 查找第一个被暂停的文件
+		for (let i = 0; i < uploadFileList.value.length; i++) {
+			const item = uploadFileList.value[i]
+			if (item.percents > 0 && item.percents < 100 && pauseFlags.value[item.md5] === true) {
+				console.log(`自动恢复 ${item.name}`)
+				pauseFlags.value[item.md5] = false
+				uploadingTasks.value[item.md5] = true
+				await uploadSingleFile(item)
+				break
+			}
+		}
+	}
 	const customRequest = () => {}
 	const getList = () => {
 		axios

+ 1 - 1
src/views/myResources/personalResources/index.vue

@@ -1,7 +1,7 @@
 <template>
 	<div style="overflow-y: auto; display: flex">
 		<!-- 主要内容区域 -->
-		<div style="flex: 1; margin-left: 20px">
+		<div style="flex: 1; margin-left: 0px">
 			<a-layout>
 				<Header @onChangeCurrent="onChangeCurrent" />
 				<div style="width: 90%; margin-left: 5%; display: flex">

+ 6 - 1
src/views/myResources/resourceUpload.vue

@@ -228,9 +228,13 @@
 	}
 	const onSuccess = (uploadFileList) => {
 		let list = []
+		console.log('formState.userfileIds是数组:', uploadFileList)
 		for (let i = 0; i < uploadFileList.length; i++) {
-			list.push(uploadFileList[i].userFileId)
+			if (uploadFileList[i].userFileId) {
+				list.push(uploadFileList[i].userFileId)
+			}
 		}
+		console.log('formState.userfileIds是:', list)
 		formState.userfileIds = list.join(',')
 	}
 
@@ -413,6 +417,7 @@
 	const handleUploadOk = async () => {
 		try {
 			await formRef.value.validate()
+			console.log('formState.userfileIds是:提交了', formState.userfileIds)
 			if (!formState.userfileIds) {
 				Modal.error({ content: '请先上传文件!' })
 				return