前端页面使用的elment-plus的el-upload组件。
当上传文件后,会调用handleChange 方法,可以在这里进行文件相关的操作。
//处理文件上传
const handleChange = async (uploadFile) => {
//文件名字
let fileName = uploadFile.name
//文件的大小
const fileSize = uploadFile.size || 0
//当前的文件对象
let fileItem = {}
fileItem.fileName = fileName
fileItem.fileSize = fileSize
fileItem.state = 1 //解码中
fileItem.progress = 0 //进度是0
fileItem.filePid = 102903232
fileItem.fileMd5 = ""
fileItem.uploadSize = 0
fileUploadList.value.addFile(fileItem)
//弹框显示
isVisible.value = true
//获得文件的md5
if (uploadFile.raw) {
await generateMD5OfFile(uploadFile.raw).then(
res => {
fileItem.fileMd5 = res
}
)
}
fileUploadList.value.addMd5(fileItem.fileName, fileItem.fileMd5)
fileUploadList.value.changeFileState(fileItem.fileName, 2)
//分片上传
let chunkTotals = Math.ceil(fileSize / chunkSize);
//分片上传
if (chunkTotals > 0) {
for (let chunkNumber = 0, start = 0; chunkNumber < chunkTotals; chunkNumber++, start += chunkSize) {
//文件最后的end
let end = Math.min(fileSize, start + chunkSize);
// el-mement - plus中,上传的文件就在raw里面
const files = uploadFile.raw?.slice(start, end)
//上传的结果
const result = await uploadFileToServer(files, chunkNumber + 1, chunkTotals, fileName , getCurrentId(), fileItem.fileMd5,userId)
console.log(result.data)
console.log(result.data.data)
if (result.data.data.status === 1) {
// console.log("上传中")
//上传的进度
fileUploadList.value.changeProgress(fileItem.fileName, ((end / fileSize) * 100).toFixed(1))
//修改已经上传完成的文件大小
fileUploadList.value.changeUploadSize(fileItem.fileName, end)
} else if (result.data.data.status === 3) {
// console.log("上传成功!"),这里是弹窗显示的文件上传进度,可以适当修改
fileUploadList.value.changeFileState(fileItem.fileName, 3) //上传完成
fileUploadList.value.changeProgress(fileItem.fileName, 100) // 进度100%
//通过main,进行刷新
$emit("addChangeNum")
return ; //结束
} else {
message("上传失败", 'error')
return; //结束
}
}
}
}
计算文件的MD5值
//计算文件的md5
function generateMD5OfFile(file) {
return new Promise((resolve, reject) => {
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, // Read in chunks of 2MB
chunks = Math.ceil(file.size / chunkSize),
currentChunk = 0,
spark = new SparkMD5.ArrayBuffer(),
fileReader = new FileReader();
fileReader.onload = function (e) {
console.log('read chunk nr', currentChunk + 1, 'of', chunks);
spark.append(e.target.result); // Append array buffer
currentChunk++;
if (currentChunk < chunks) {
loadNext();
} else {
resolve(spark.end())
}
};
fileReader.onerror = function () {
reject('MD5 calc error')
};
function loadNext() {
let start = currentChunk * chunkSize,
end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
loadNext();
})
}
自定义文件切片大小
//默认分片大小
const chunkSize = 5 * 1024 * 1024
上传文件到服务器
// 上传文件到服务器
const uploadFileToServer = async (file, chunkNumber, chunkTotal, fileName,filePid, fileMd5,userId) => {
const form = new FormData();
// 这里的data是文件
form.append("file", file);
form.append("chunkNumber", chunkNumber);
form.append("chunkTotal", chunkTotal);
form.append("fileName", fileName)
form.append("fileMd5", fileMd5)
form.append("filePid", filePid)
form.append("userId", userId)
var result = await axios({
url: env_server_production + '/file/upload',
headers: { 'Content-Type': 'multipart/form-data' },
method: "post",
timeout: 1000000,
data: form
})
return result
}
可以简单的实现对一些文件的预览,比如图片、视频、word、pdf等等。
pdf:
等等
这里使用的是vue-office
点击图片查看详情
该文件不支持在线浏览,请下载后查看!
{{ documentContent }}
后端使用minio,minio先接收分片文件,上传完成所有的分片文件后,在合并分片文件,删除中间文件即可。
/**
* 上传文件方法。
* 该方法负责检查文件是否已存在,如果存在,则返回已存在标志;如果不存在且是完整文件,则上传文件到MinIO并保存文件信息到数据库。
*
* @param fileVO 文件相关信息VO,包含文件本身、MD5、文件名等。
* @return 如果文件已存在,返回秒传状态码;如果文件上传完成,返回上传完成状态码;否则返回null。
* @throws GeneralException 如果文件为空,抛出通用异常。
*/
@Override
@Transactional(rollbackFor = Exception.class) //所有的操作都在一个事务里面。
public HashMap
一个文件有个不重复的md5值,所谓的秒传其实就是你要上传的文件,别人已经上传过了,minio中已经有这个文件了,再解析完文件的md5值之后,后端发现数据库中md5存在了,所以就不用上传文件了,直接在数据库中创建一个信息即可,也就实现了秒传。
传统传递过程是一整个文件上传,如果中断了下次传的时候,需要重新上传;断点传递,每次传递的时候,可以把分片信息放到redis中,同时下一次传分片的时候,判断一下,redis中时候已经有了这个分片,如果有就不用上传此分片文件,即断点传递。