断点续传(Resumable Transfer)是一种在数据传输过程中,如果发生中断或失败,可以从中断的地方继续传输的技术。它主要用于文件传输和下载,以提高数据传输的可靠性和效率。
断点续传的实现可以概括为如下几个步骤:
1. 客户端请求文件
客户端向服务器请求一个文件的下载,通常会包括一个表示文件的起始位置的请求头(如 Range
头),告诉服务器从哪个位置开始传输。
2. 服务器处理请求
服务器接收到请求后,根据请求头中的范围信息,决定从文件的哪个位置开始传输。如果文件支持断点续传,服务器会返回部分文件数据,并在响应头中包含相关的范围信息(如 Content-Range
)。
3. 客户端接收数据
客户端接收数据并保存到本地。如果下载中断,客户端会记录当前下载进度(通常保存在一个临时文件或数据库中)。
4. 恢复下载
当下载中断后,客户端会从记录的进度位置重新发起请求,继续下载未完成的部分。服务器根据记录的进度,返回从中断位置开始的文件数据。
用比较简单、通俗的话来解释,断点续传就是把数据拆成一个个完整的小文件,给每个小文件加编号,每次传输一个文件,若产生中断就检查剩下未传输的文件,继续传输,不用再从头开始。这样每一个小文件都是完整的,接收方记录收到的序号,根据序号检查未传输成功的数据,即可知道哪些文件没有传输成功。断点续传是由服务器给客户端一个已经上传的位置标记position,然后客户端再将文件指针移动到相应的position,通过输入流将文件剩余部分读出来传输给服务器,断点下载是由客户端告诉服务器已经下载的大小,然后服务器会将指针移动到相应的position,继续读出,把文件返回给客户端。
实现断点续传的关键点在于服务器需要能够处理 HTTP Range
头,支持范围请求。对于大多数现代 Web 服务器(如 Nginx、Apache)和存储服务(如 AWS S3),这一功能都是内置的;其次客户端需要记录下载进度。这可以通过保存已下载的文件大小或下载进度的元数据来实现;再次,在恢复下载时,客户端可能需要验证数据的完整性,确保下载过程中没有数据损坏;最后,处理网络中断或其他错误时,需要能够重新发起请求并恢复下载。
我们可以使用一段代码演示一下断点续传的实现:
import java.io.*; // 导入输入输出流相关的类
import java.net.HttpURLConnection; // 导入处理 HTTP 连接的类
import java.net.URL; // 导入处理 URL 的类
public class ResumableDownload {
public static void main(String[] args) {
// 文件下载的 URL
String fileUrl = "http://example.com/file.zip";
// 本地保存文件的路径
String localFilePath = "file.zip";
// 计算本地文件的已下载大小,如果文件存在,则获取其长度;否则设置为 0
long downloadedSize = new File(localFilePath).exists() ? new File(localFilePath).length() : 0;
try {
// 创建 URL 对象
URL url = new URL(fileUrl);
// 打开 HTTP 连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置 Range 头,指定从已下载的字节位置开始下载
connection.setRequestProperty("Range", "bytes=" + downloadedSize + "-");
// 获取输入流以读取从服务器接收到的数据
try (InputStream inputStream = connection.getInputStream();
// 创建 RandomAccessFile 对象以允许从指定位置写入数据
RandomAccessFile outputFile = new RandomAccessFile(localFilePath, "rw")) {
// 将文件指针移动到已下载的字节位置
outputFile.seek(downloadedSize);
// 定义缓冲区以进行读取操作
byte[] buffer = new byte[4096];
int bytesRead;
// 读取数据并写入文件
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputFile.write(buffer, 0, bytesRead);
}
}
System.out.println("Download completed successfully!");
} catch (IOException e) {
// 捕获并处理 IO 异常
e.printStackTrace();
}
}
}
当然,这只是一个简单的demo,演示了一下断点续传的原理,远远不能满足实际业务需要,感兴趣的同学可以参考一下这篇文章文件断点续传,自行学习一下客户端代码和服务端代码的业务逻辑。