赞
踩
大家好,我是程序员阿药。今天和大家分享的是一个上传文件的方式:断点续传。
较大的文件一次性上传会比较慢,中途退出或网络延迟、故障等,容易出现超时等问题。待问题修复后再次上传时仍需要从头开始上传。
断点续传允许在上传中断后继续上传,而无需从头开始。
断点续传需要先把上传文件按一定大小分块,然后将所有分块上传,最后通过上传的所有分块合并出原文件。如图:
1. 执行文件分块(文件分块)、2. 检查分块是否已经上传、3. 上传分块、4. 合并分块
如图:
- public void uploadFile() throws IOException {
- // 原文件路径
- String sourcePath = "D:\\Data\\videos\\test.mp4";
- // 本地分块文件存储路径
- String localChunkFilePath = "D:\\Data\\chunk\\";
- // 原文件md5
- String sourceMd5 = DigestUtils.md5DigestAsHex(new FileInputStream(sourcePath));
- // 1.执行文件分块,返回分块总数
- int chunkTotal = fileToChunk(sourcePath, localChunkFilePath);
- int i = 0;
- for (; i < chunkTotal; i++) {
- // 2.检查分块是否已经上传
- boolean isSuccess = uploadController.checkChunks(sourceMd5, i);
- if (isSuccess) {
- System.out.println(i + "号分块已经上传成功");
- } else {
- // 3.上传分块
- isSuccess = uploadController.uploadChunks(sourceMd5, i, localChunkFilePath + i);
- if (isSuccess) {
- System.out.println("执行上传" + i + "号分块成功");
- } else {
- System.out.println("执行上传" + i + "号分块失败");
- break;
- }
- }
- }
- // 4.合并分块
- if (i == chunkTotal) {
- System.out.println("所有分块均上传成功,开始合并分块");
- boolean isSuccess = uploadController.mergeChunks(sourceMd5, chunkTotal, sourcePath);
- if (isSuccess) {
- System.out.println("合并分块成功");
- } else {
- System.out.println("合并分块失败");
- }
- }
- }
-
- /**
- * 检查分块是否已经上传
- * @param fileMd5 文件md5
- * @param chunkIndex 分块序号
- */
- public boolean checkChunks(String fileMd5, int chunkIndex) {
- // 根据md5得到分块文件所在目录的路径
- String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
- // 分块路径
- String chunkPath = chunkFileFolderPath + chunkIndex;
- // 判断分块是否已经存在
- StatObjectArgs statObjectArgs = StatObjectArgs.builder()
- .bucket(bucket)
- .object(chunkPath)
- .build();
- try {
- minioClient.statObject(statObjectArgs);
- } catch (Exception e) {
- // 分块不存在
- return false;
- }
- // 分块存在
- return true;
- }
-
- /**
- * 上传分块
- * @param fileMd5 文件md5
- * @param chunkIndex 分块序号
- * @param localChunkFilePath 分块文件本地路径
- */
- public boolean uploadChunks(String fileMd5, int chunkIndex, String localChunkFilePath) {
- // 分块路径
- String chunkPath = getChunkFileFolderPath(fileMd5) + chunkIndex;
- // 获取文件的mimeType
- String mimeType = getMimeType(".temp");
- // 将分块文件上传到minio
- try {
- UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
- .bucket(bucket) // 存储桶
- .filename(localChunkFilePath) // 分块文件本地路径
- .object(chunkPath) // 对象名(分块路径)
- .contentType(mimeType) // 设置媒体文件类型
- .build();
- // 将分块从本地 localChunkFilePath 位置上传到 bucket 桶中的 chunkPath 位置
- minioClient.uploadObject(uploadObjectArgs);
- } catch (Exception e) {
- // 上传失败
- return false;
- }
- // 上传成功
- return true;
- }
-
- /**
- * 合并分块
- * @param fileMd5 文件md5
- * @param chunkTotal 分块总数
- * @param sourceFileName 原文件名称
- */
- public boolean mergeChunks(String fileMd5, int chunkTotal, String sourceFileName) {
- // 分块文件所在目录
- String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
- // 找到所有的分块文件
- List<ComposeSource> sources = Stream.iterate(0, i -> ++i)
- .limit(chunkTotal)
- .map(i -> ComposeSource.builder().bucket(bucket).object(chunkFileFolderPath + i).build())
- .collect(Collectors.toList());
- // 源文件扩展名
- String extension = sourceFileName.substring(sourceFileName.lastIndexOf("."));
- // 分块合并后文件的路径(对象名)
- String objectName = getFilePathByMd5(fileMd5, extension);
- // 指定合并后文件的objectName等信息
- ComposeObjectArgs composeObjectArgs = ComposeObjectArgs.builder()
- .bucket(bucket)
- .object(objectName) // 合并后文件的路径(对象名)
- .sources(sources) // 所有分块
- .build();
- // 合并文件
- // 报错size 1048576 must be greater than 5242880,minio默认的分块文件大小为5M
- try {
- minioClient.composeObject(composeObjectArgs);
- } catch (Exception e) {
- return false;
- }
- // 校验合并后的和源文件是否一致,一致则上传成功
- // 先下载合并后的文件
- File file = downloadFileFromMinIO(objectName);
- try (FileInputStream fileInputStream = new FileInputStream(file)) {
- // 计算合并后文件的md5
- String mergeFileMd5 = DigestUtils.md5DigestAsHex(fileInputStream);
- // 比较原始md5和合并后文件的md5
- if (!fileMd5.equals(mergeFileMd5)) {
- // 合并失败
- return false;
- }
- } catch (Exception e) {
- // 合并失败
- return false;
- }
- // 合并成功
- return true;
- }
注意:报错size 1048576 must be greater than 5242880(Minio默认的分块文件大小为5M)
1. 上传过程未中断。文件分块后全部上传成功,且分块合并原文件成功。如图:
2. 制造上传分块过程中断场景,将已经上传的6号分块及以后分块全部删除,同时删除合并后的文件。如图:
再次运行代码,查看分块是否可以从断点处(6号分块)开始上传。测试结果如图:
今天的分享到此结束啦,喜欢的小伙伴可以点赞关注支持一下。
有需要全部源码的可以关注我的微信公众号:”程序员阿药“,回复:”断点续传“ 即可获得全部源码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。