如何实现大文件断点续传、秒传
大家先来了解一下几个概念:
-
「文件分块」:将大文件拆分成小文件,将小文件上传\下载,最后再将小文件组装成大文件;
-
「断点续传」:在文件分块的基础上,将每个小文件采用单独的线程进行上传\下载,如果碰到网络故障,可以从已经上传\下载的部分开始继续上传\下载未完成的部分,而没有必要从头开始上传\下载;
-
「文件秒传」:资源服务器中已经存在该文件,其他人上传时直接返回该文件的URI。
1、RandomAccessFile
平时我们都会使用FileInputStream,FileOutputStream,FileReader以及FileWriter等IO流来读取文件,今天我们来了解一下RandomAccessFile。
它是一个直接继承Object的独立的类,底层实现中它实现的是DataInput和DataOutput接口。该类支持随机读取文件,随机访问文件类似于文件系统中存储的大字节数组。
它的实现基于「文件指针」(一种游标或者指向隐含数组的索引),文件指针可以通过getFilePointer方法读取,也可以通过seek方法设置。
输入时从文件指针开始读取字节,并使文件指针超过读取的字节,如果写入超过隐含数组当前结尾的输出操作会导致扩展数组。该类有四种模式可供选择:
- r: 以只读方式打开文件,如果执行写入操作会抛出IOException;
- rw: 以读、写方式打开文件,如果文件不存在,则尝试创建文件;
- rws: 以读、写方式打开文件,要求对文件内容或元数据的每次更新都同步写入底层存储设备;
- rwd: 以读、写方式打开文件,要求对文件内容的每次更新都同步写入底层存储设备;
在rw模式下,默认是使用buffer的,只有cache满的或者使用RandomAccessFile.close()关闭流的时候才真正的写到文件。
1.1、API
1、void seek(long pos):设置下一次读取或写入时的文件指针偏移量,通俗点说就是指定下次读文件数据的位置。
偏移量可以设置在文件末尾之外,只有在偏移量设置超出文件末尾后,才能通过写入更改文件长度;
2、native long getFilePointer():返回当前文件的光标位置;
3、native long length():返回当前文件的长度;
4、「读」方法

5、「写」方法

6、readFully(byte[] b):这个方法的作用就是将文本中的内容填满这个缓冲区b。如果缓冲b不能被填满,那么读取流的过程将被阻塞,如果发现是流的结尾,那么会抛出异常;
7、FileChannel getChannel():返回与此文件关联的唯一FileChannel对象;
8、int skipBytes(int n):试图跳过n个字节的输入,丢弃跳过的字节;
RandomAccessFile的绝大多数功能,已经被JDK1.4的NIO的「内存映射」文件取代了,即把文件映射到内存后再操作,省去了频繁磁盘io。
2、文件分块
文件分块需要在前端进行处理,可以利用强大的js库或者现成的组件进行分块处理。需要确定分块的大小和分块的数量,然后为每一个分块指定一个索引值。
为了防止上传文件的分块与其它文件混淆,采用文件的md5值来进行区分,该值也可以用来校验服务器上是否存在该文件以及文件的上传状态。
- 如果文件存在,直接返回文件地址;
- 如果文件不存在,但是有上传状态,即部分分块上传成功,则返回未上传的分块索引数组;
- 如果文件不存在,且上传状态为空,则所有分块均需要上传。
fileRederInstance.readAsBinaryString(file);
fileRederInstance.addEventListener("load", (e) => {let fileBolb = e.target.result;fileMD5 = md5(fileBolb);const formData = new FormData();formData.append("md5", fileMD5);axios.post(http + "/fileUpload/checkFileMd5", formData).then((res) => {if (res.data.message == "文件已存在") {//文件已存在不走后面分片了,直接返回文件地址到前台页面success && success(res);} else {//文件不存在存在两种情况,一种是返回data:null代表未上传过 一种是data:[xx,xx] 还有哪几片未上传if (!res.data.data) {//还有几片未上传情况,断点续传chunkArr = res.data.data;}readChunkMD5();}}).catch((e) => {});
});
在调用上传接口前,通过slice方法来取出索引在文件中对应位置的分块。
const getChunkInfo = (file, currentChunk, chunkSize) => {//获取对应下标下的文件片段let start = currentChunk * chunkSize;let end = Math.min(file.size, start + chunkSize);//对文件分块let chunk = file.slice(start, end);return { start, end, chunk };};
之后调用上传接口完成上传。
3、断点续传、文件秒传
后端基于spring boot开发,使用redis来存储上传文件的状态和上传文件的地址。
如果文件完整上传,返回文件路径;部分上传则返回未上传的分块数组;如果未上传过返回提示信息。
在上传分块时会产生两个文件,一个是文件主体,一个是临时文件。临时文件可以看做是一个数组文件,为每一个分块分配一个值为127的字节。
校验MD5值时会用到两个值:
- 文件上传状态:只要该文件上传过就不为空,如果完整上传则为true,部分上传返回false;
- 文件上传地址:如果文件完整上传,返回文件路径;部分上传返回临时文件路径。
/*** 校验文件的MD5**/
public Result checkFileMd5(String md5) throws IOException {//文件是否上传状态:只要该文件上传过该值一定存在Object processingObj = stringRedisTemplate.opsForHash().get(UploadConstants.FILE_UPLOAD_STATUS, md5);if (processingObj == null) {return Result.ok("该文件没有上传过");}boolean processing = Boolean.parseBoolean(processingObj.toString());//完整文件上传完成时为文件的路径,如果未完成返回临时文件路径(临时文件相当于数组,为每个分块分配一个值为127的字节)String value = stringRedisTemplate.opsForValue().get(UploadConstants.FILE_MD5_KEY + md5);//完整文件上传完成是true,未完成返回falseif (processing) {return Result.ok(value,"文件已存在");} else {File confFile = new File(value);byte[] completeList = FileUtils.readFileToByteArray(confFile);List<Integer> missChunkList = new LinkedList<>();for (int i = 0; i < completeList.length; i++) {if (completeList[i] != Byte.MAX_VALUE) {//用空格补齐missChunkList.add(i);}}return Result.ok(missChunkList,"该文件上传了一部分");}
}
说到这,你肯定会问:当这个文件的所有分块上传完成之后,该怎么得到完整的文件呢?接下来我们就说一下分块合并的问题。
4、分块上传、文件合并
上边我们提到了利用文件的md5值来维护分块和文件的关系,因此我们会将具有相同md5值的分块进行合并,由于每个分块都有自己的索引值,所以我们会将分块按索引像插入数组一样分别插入文件中,形成完整的文件。
分块上传时,要和前端的分块大小、分块数量、当前分块索引等对应好,以备文件合并时使用,此处我们采用的是「磁盘映射」的方式来合并文件。
//读操作和写操作都是允许的
RandomAccessFile tempRaf = new RandomAccessFile(tmpFile, "rw");
//它返回的就是nio通信中的file的唯一channel
FileChannel fileChannel = tempRaf.getChannel();//写入该分片数据 分片大小 * 第几块分片获取偏移量
long offset = CHUNK_SIZE * multipartFileDTO.getChunk();
//分片文件大小
byte[] fileData = multipartFileDTO.getFile().getBytes();
//将文件的区域直接映射到内存
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, offset, fileData.length);
mappedByteBuffer.put(fileData);
// 释放
FileMD5Util.freedMappedByteBuffer(mappedByteBuffer);
fileChannel.close();
每当完成一次分块的上传,还需要去检查文件的上传进度,看文件是否上传完成。
RandomAccessFile accessConfFile = new RandomAccessFile(confFile, "rw");
//把该分段标记为 true 表示完成
accessConfFile.setLength(multipartFileDTO.getChunks());
accessConfFile.seek(multipartFileDTO.getChunk());
accessConfFile.write(Byte.MAX_VALUE);//completeList 检查是否全部完成,如果数组里是否全部都是(全部分片都成功上传)
byte[] completeList = FileUtils.readFileToByteArray(confFile);
byte isComplete = Byte.MAX_VALUE;
for (int i = 0; i < completeList.length && isComplete == Byte.MAX_VALUE; i++) {//与运算, 如果有部分没有完成则 isComplete 不是 Byte.MAX_VALUEisComplete = (byte) (isComplete & completeList[i]);
}
accessConfFile.close();
然后更新文件的上传进度到Redis中。
//更新redis中的状态:如果是true的话证明是已经该大文件全部上传完成
if (isComplete == Byte.MAX_VALUE) {stringRedisTemplate.opsForHash().put(UploadConstants.FILE_UPLOAD_STATUS, multipartFileDTO.getMd5(), "true");stringRedisTemplate.opsForValue().set(UploadConstants.FILE_MD5_KEY + multipartFileDTO.getMd5(), uploadDirPath + "/" + fileName);
} else {if (!stringRedisTemplate.opsForHash().hasKey(UploadConstants.FILE_UPLOAD_STATUS, multipartFileDTO.getMd5())) {stringRedisTemplate.opsForHash().put(UploadConstants.FILE_UPLOAD_STATUS, multipartFileDTO.getMd5(), "false");}if (!stringRedisTemplate.hasKey(UploadConstants.FILE_MD5_KEY + multipartFileDTO.getMd5())) {stringRedisTemplate.opsForValue().set(UploadConstants.FILE_MD5_KEY + multipartFileDTO.getMd5(), uploadDirPath + "/" + fileName + ".conf");}
}相关文章:
如何实现大文件断点续传、秒传
大家先来了解一下几个概念: 「文件分块」:将大文件拆分成小文件,将小文件上传\下载,最后再将小文件组装成大文件; 「断点续传」:在文件分块的基础上,将每个小文件采用单独的线程进行上传\下载&…...
备战蓝桥python——完全平方数
完全平方数 链接: 完全平方数 暴力解法: n int(input()) for i in range(1, n1):if(((i*n)**0.5)%10.0):print(i)break运用数论相关知识求解 任意一个正整数都可以被分解成若干个质数乘积的形式,例如 :2022∗5120 \ 2^{2}*5^{1}\,20 22∗51 由此…...
WebRTC中的NAT穿透
NAT简介 我们知道,WebRTC会按照内网、P2P、中转的顺序来尝试连接。在大部分的情况下,实际是使用P2P或者中转的。这里P2P的场景主要使用的技术就是NAT穿透。 我们先简单了解下NAT。NAT在真实网络中是常见的,它的出现一是为了解决ipv4地址不够…...
SpringCloud-高级篇(一)
目录: (1)初识Sentinel-雪崩问题的解决方案 (2)服务保护Sentinel和Hystrix对比 (3)Sentinel初始-安转控制台 (4)整合微服务和Sentinel 微服务高级篇 (1&…...
电脑自动重启是什么原因?详细解说
案例:电脑自动重启是什么原因? “一台用了一年的电脑,最近使用,每天都会一两次莫名其妙自动重启,看了电脑错误日志,看不懂什么意思,一直找不到答案。有没有高手知道怎么解决这个问题的。” 当…...
2023美国大学生数学建模竞赛E题思路
problem 背景: 光污染用于描述过度或不良使用人造光。我们称之为光污染的一些现象包括光侵入、过度照明和光杂波。在大城市,太阳落山后,这些现象最容易在天空中看到:然而,它们也可能发生在更偏远的地区。 光污染会改变我们对夜空…...
蓝桥杯三月刷题 第五天
文章目录💥前言😉解题报告💥数的分解🤔一、思路:😎二、代码:💥前言 上午没写,下午写了会被朋友拉出去耍,被冷风吹到了,而且被他坑了,根本没有玩骑…...
Echarts 水波图实现
开发的项目中需要实现这样一个水波图,例如下图在echarts官网中找了很久没找到,后面是在Echarts社区中找到的,实现了大部分的样式,但是还有一些数据的展示没有实现。水波图的数值展示是默认整数百分比,我的需求是需要保…...
逻辑优化基础-shannon decomposition
1. 简介 在逻辑综合中,香农分解(Shannon decomposition)是一种常用的布尔函数分解方法。它将一个布尔函数分解为两个子函数的和,其中每个子函数包含一个布尔变量的取反和非取反的部分。 具体来说,假设对于一个布尔函…...
Java中线程池的创建与使用
前言:默认线程池的弊端在线程池应用中,参考阿里巴巴java开发规范:线程池不允许使用Executors去创建,不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确&…...
关于HashMap与OkHttp的使用
写了一个okhttp的post请求方法,添加参数很麻烦,需要封装: //post请求public static void sendOkHttpRequestPost(String address , Callback callback) {OkHttpClient client new OkHttpClient();// 创建表单参数RequestBodyRequestBody fo…...
华为OD机试 - 单词倒序(C 语言解题)【独家】
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 使用说明本期题目:单词倒序…...
搭建Samba服务器
搭建Samba服务器 文章目录搭建Samba服务器samba安装安装命令配置-ubuntu侧为samba服务器创建一个共享目录share创建使用该共享文件夹的账号修改samba服务器配置文件重启samba服务windows创建映射1.点击映射网络驱动器2.输入Ubuntu中的ip地址及其用户信息3.输入用户信息及其密码…...
Matlab进阶绘图第5期—风玫瑰图(WindRose)
风玫瑰图(Wind rose diagram)是一种特殊的极坐标堆叠图/统计直方图,其能够直观地表示某个地区一段时期内风向、风速的发生频率。 风玫瑰图在建筑规划、环保、风力发电、消防、石油站设计、海洋气候分析等领域都有重要作用,所以在一些顶级期刊中也能够看…...
【SQL开发实战技巧】系列(二十四):数仓报表场景☞通过执行计划详解”行转列”,”列转行”是如何实现的
系列文章目录 【SQL开发实战技巧】系列(一):关于SQL不得不说的那些事 【SQL开发实战技巧】系列(二):简单单表查询 【SQL开发实战技巧】系列(三):SQL排序的那些事 【SQL开发实战技巧…...
XILINX AXI总线学习
AXI介绍什么是AXI?AXI(高级可扩展接口),是ARM AMBA的一部分;AMBA:高级微控制器总线架构;是1996年首次引入的一组微控制器总线;开放的片内互联的总线标准,能在多主机设计中实现多个控…...
2022CCPC女生赛(补题)(A,C,E,G,H,I)
迟了好久的补题,,现在真想把当时赛时的我拉出来捶一拳排序大致按照题目难度。C. 测量学思路:直接循环遍历判断即可,注意角度要和2π取个最小值。AC Code:#include <bits/stdc.h>typedef long long ll; const int…...
【Nginx】Nginx的安装配置
环境说明系统:Centos 7一、编译安装Nginx官网下载地址nginx: download#安装依赖 [rootnginx nginx-1.22.1]# yum install gcc pcre pcre-devel zlib zlib-devel -y #从官网下载Nginx安装包,并进行解压、编译、安装 [rootnginx ~]# wget https://nginx.or…...
数学小课堂:统计时有效地筛选数据
文章目录引言I 被爆冷门的原因II 统计时有效地筛选数据2.1 统计数据的常见问题2.2 大数据的特征2.3 有效筛选数据的原则引言 在博弈论中很多结果有发生的概率,而概率这件事只是估计出来的,并不准确。因此,一旦加入博弈的选手多了之后&#x…...
MySQL安装优化
hello,大家好,我是小鱼 本文主要通过针对 MySQL Server(mysqld)相关实现机制的分析,得到一些相应的优化建议。主要 涉及 MySQL 的安装以及相关参数设置的优化,但不包括 mysqld 之外的比如存储引擎相关的参…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
