项目难点——断点续传分片上传
Posted NPE~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目难点——断点续传分片上传相关的知识,希望对你有一定的参考价值。
项目难点——【2】断点续传、分片上传
1 文件分片
在网络请求中,如果我们有时是上传大文件,可能由于网络原因,导致上传断断续续,很难一次性上传成功,那么这个时候我们就需要将大文件分块,分成一小块一小块的,然后结合端点续传技术,实现大文件上传。
2 断点续传
2.1 概念
通常视频文件都比较大,所以对于媒资系统上传文件的需求要满足大文件的上传要求。http协议本身对上传文件大小没有限制,但是客户的网络环境质量、电脑硬件环境等参差不齐,如果一个大文件快上传完了网断了没有上传完成,需要客户重新上传,用户体验非常差,所以对于大文件上传的要求最基本的是断点续传。
断点续传效果图:
流程如下:
1、前端上传前先把文件分成块
2、一块一块的上传,上传中断后重新上传,已上传的分块则不用再上传
3、各分块上传完成最后在服务端合并文件
2.2 分片与合并测试
①文件分片
具体流程:
1. 获取源文件长度
2. 根据设定的分块文件大小计算块数
3. 从源文件读数据依次向每一个块文件写入数据
/**
* @author zhouYi
* @description 大文件测试处理
* @date 2023/2/17 8:34
*/
public class BigFileTest
//测试文件分片
@Test
public void testChunk() throws IOException
File sourceFile = new File("D:\\\\系统默认\\\\桌面\\\\gra-res\\\\mv\\\\test.mp4");
//分片文件路径
String chunkPath = "d:/chunk/";
File chunkFolder = new File(chunkPath);
if(!chunkFolder.exists())
chunkFolder.mkdirs();
//分片文件大小
long chunkSize = 1024 * 1024 * 1;
//分片数量[向上取整,1.5M则需要两块分片传输]
long chunkNum = (long) Math.ceil(sourceFile.length() * 1.0 / chunkSize);
System.out.println("分块总数:" + chunkNum);
//缓冲区大小
byte[] b = new byte[1024];
//使用RandomAccessFile访问文件["r",读]
RandomAccessFile raf_read = new RandomAccessFile(sourceFile, "r");
//将文件分片
for (int i = 0; i < chunkNum; i++)
//创建分片文件
File file = new File(chunkPath + i);
if(file.exists())
file.delete();
boolean newFile = file.createNewFile();
if(newFile)
//如果创建成功则向分片文件中写入数据
RandomAccessFile raf_write = new RandomAccessFile(file, "rw");
int len = -1;
while((len = raf_read.read(b)) != -1)
//从缓冲区读取数据
raf_write.write(b, 0, len);
if(file.length() >= chunkSize)
break;
raf_write.close();
System.out.println("完成分片" + i);
raf_read.close();
结果:
②文件合并
- 合并流程:
1、找到要合并的文件并按文件合并的先后进行排序。
2、创建合并文件
3、依次从合并的文件中读取数据向合并文件写入数
- 具体代码:
@Test
public void testMerge() throws IOException
//片文件目录
File chunkFolder = new File("d:/chunk/");
//原始文件
File originalFile = new File("D:\\\\系统默认\\\\桌面\\\\gra-res\\\\mv\\\\test.mp4");
//合并后的文件
File mergeFile = new File("D:\\\\系统默认\\\\桌面\\\\gra-res\\\\mv\\\\test2.mp4");
if(mergeFile.exists())
mergeFile.delete();
//创建新的合并文件
mergeFile.createNewFile();
//用于写文件
RandomAccessFile raf_write = new RandomAccessFile(mergeFile, "rw");
//指针指向文件顶端
raf_write.seek(0);
//缓冲区
byte[] b = new byte[1024];
//分块列表
File[] fileArray = chunkFolder.listFiles();
//转成集合便于排序【保证数据正确,以便可以恢复数据】
List<File> fileList = Arrays.asList(fileArray);
//从小到大排序
Collections.sort(fileList, Comparator.comparingInt(o -> Integer.parseInt(o.getName())));
//合并文件
for (File chunkFile : fileList)
RandomAccessFile raf_read = new RandomAccessFile(chunkFile, "rw");
int len = -1;
while((len = raf_read.read(b)) != -1)
raf_write.write(b, 0, len);
raf_read.close();
raf_write.close();
//校验文件是否成功恢复【如果文件相同,则二者MD5值相同】
try(
FileInputStream fileInputStream = new FileInputStream(originalFile);
FileInputStream mergeFileStream = new FileInputStream(mergeFile);
)
//原始文件MD5
String originalMd5 = DigestUtils.md5Hex(fileInputStream);
//合并文件的md5
String mergeFileMd5 = DigestUtils.md5Hex(mergeFileStream);
if(originalMd5.equals(mergeFileMd5))
System.out.println("合并文件成功");
else
System.out.println("合并文件失败");
成功播放:
3 实现思路
3.1 前端对文件分块
3.2 前端校验并上传
3.3 前端发送合并请求
3.4 服务端传到分布式文件系统
3.5 总结
- 前端对文件分块
- 前端使用多线程一块一块上传,上传前给服务器发一个消息校验该分块是否上传,如果已经上传则不再上传
- 等所有分块上传完毕,服务端合并所有分块,校验文件的完整性(因为分块全部上传到了服务器器,服务器所有分块按顺序进行合并,就是写每个分块文件内容按顺序依次写入一个文件中,使用字节流去读写文件)
- 前端给服务传了一个md5值,服务端合并文件后计算合并文件的md5是否和前端传的一样,如果一样则说明文件完整,如果不一样说明可能由于网络丢包导致文件不完整,这是上传失败需要重新上传
- 分块文件清理?
上传一个文件进行分块上传,上传一般不传了,之前上传到minio的分块文件要清理吗?怎么做的?
- 在数据库中有一张表表记录minio中存储的文件信息
- 文件开始上传时会写入文件表,状态为上传中,上传完成会更新状态为上传完成
- 当一个文件传了一般不再上传了说明该文件没有上传完成,会有定时任务去查询文件表中的记录,如果文件未上传完成则删除minio中没有上传成功的文件目录
以上是关于项目难点——断点续传分片上传的主要内容,如果未能解决你的问题,请参考以下文章