1. 介绍
HTTP断点续传是一种通过多次请求,将文件分块传输的技术。这样,如果在传输过程中发生了中断,可以从中断处继续传输,而不需要重新开始。这对于大文件的下载是一种有效的优化手段,提高了下载的稳定性和效率。
2. 原理
HTTP断点续传的原理基于HTTP协议的能力和服务器的支持。当客户端需要下载一个文件时,通常会发送一个HTTP请求到服务器来获取文件内容。在断点续传的情况下,这个过程有所不同。
Range
头部字段: HTTP协议中的Range头部字段允许客户端请求服务器发送文件的指定范围,而不是整个文件。这个范围可以通过字节偏移量来指定,比如从哪个字节到哪个字节的范围。- 服务器支持: 为了实现断点续传,服务器需要支持处理这样的部分请求。服务器接收到这个带有Range头部字段的请求后,会识别并返回请求的部分内容,而不是整个文件。
- 客户端处理: 客户端收到部分内容后,可以将这些部分数据拼接成完整的文件。如果下载中断,客户端可以发送新的请求,请求中包含之前下载过的字节范围以及后续需要的字节范围,从而继续下载。
3. 简单实现
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class FileDownloader {
private static final String TAG = "FileDownloader";
private static final int BUFFER_SIZE = 4096;
private String downloadUrl;
private String destinationPath;
private DownloadListener downloadListener;
public FileDownloader(String downloadUrl, String destinationPath, DownloadListener downloadListener) {
this.downloadUrl = downloadUrl;
this.destinationPath = destinationPath;
this.downloadListener = downloadListener;
}
public void startDownload() {
new Thread(new Runnable() {
@Override
public void run() {
downloadFile();
}
}).start();
}
private void downloadFile() {
HttpURLConnection connection = null;
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
URL url = new URL(downloadUrl);
connection = (HttpURLConnection) url.openConnection();
// 设置部分下载的起始点
File destinationFile = new File(destinationPath);
//获取了已下载文件的大小
long downloadedSize = destinationFile.length();
//告诉服务器只发送文件中指定范围的字节,即从 downloadedSize 字节位置开始继续下载。这是 HTTP 断点续传的关键。
connection.setRequestProperty("Range", "bytes=" + downloadedSize + "-");
connection.connect();
// 检查服务器是否支持部分内容
int responseCode = connection.getResponseCode();
if (responseCode / 100 != 2 && responseCode != 206) {
// 服务器不支持部分内容,从头开始
downloadedSize = 0;
}
int fileLength = connection.getContentLength() + (int) downloadedSize;
inputStream = connection.getInputStream();
//创建了一个输出流,将数据写入到 destinationFile 文件中。
// 第二个参数 true 表示以追加模式打开文件,即如果文件已经存在,将在文件末尾追加新的数据。
outputStream = new FileOutputStream(destinationFile, true);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
downloadedSize += bytesRead;
// 更新进度
if (downloadListener != null) {
int progress = (int) (downloadedSize * 100 / fileLength);
downloadListener.onProgressUpdate(progress);
}
}
// 下载完成
if (downloadListener != null) {
downloadListener.onDownloadComplete();
}
} catch (IOException e) {
Log.e(TAG, "Error downloading file: " + e.getMessage());
} finally {
try {
if (inputStream != null) inputStream.close();
if (outputStream != null) outputStream.close();
if (connection != null) connection.disconnect();
} catch (IOException e) {
Log.e(TAG, "Error closing resources: " + e.getMessage());
}
}
}
public interface DownloadListener {
void onProgressUpdate(int progress);
void onDownloadComplete();
}
}
==需要注意== 关于断线续传的HTTP状态码不是200,而是206,如果服务器返回的是200,则不支持续传,此时进行全覆盖即从头开始下载!