Android中基于UrlConnection实现Zip文件断点续传方案咨询
用UrlConnection实现Zip文件断点续传方案
我来帮你搞定这个断点续传的问题!其实核心就是利用HTTP协议的Range请求头,让服务器从你上次中断的位置继续发送数据,下面一步步给你讲清楚实现思路和具体代码:
核心原理
断点续传的关键是HTTP的Range请求头,它允许客户端向服务器请求资源的某一段字节。当服务器支持这个特性时,会返回206 Partial Content状态码(而不是常规的200 OK),同时通过Content-Range响应头告诉客户端返回的字节范围和文件总大小。这样我们就能在本地已下载的文件基础上,继续追加后续的字节内容。
具体实现步骤
读取已下载进度:
- 检查本地是否有未完成的下载文件,如果有,获取它的字节长度作为已下载进度;
- 建议额外用一个临时文件(比如和下载文件同名的
.download-progress文件)记录当前下载进度,这样即使应用意外关闭,下次启动也能准确读取进度(避免文件被篡改导致的进度不一致)。
配置Range请求头:
- 建立UrlConnection连接后,设置请求头:
connection.setRequestProperty("Range", "bytes=" + 已下载字节数 + "-"),这里的-表示从指定字节位置开始,一直到文件末尾。
- 建立UrlConnection连接后,设置请求头:
处理服务器响应:
- 如果响应码是
206,说明服务器支持断点续传,直接读取输入流追加到本地文件; - 如果响应码是
200,说明服务器不支持Range请求,只能从头开始下载; - 如果响应码是
416,表示请求的范围无效(比如已下载的字节数超过了服务器上文件的总大小),这时候需要删除本地文件重新下载。
- 如果响应码是
追加写入本地文件:
- 打开文件输出流时,使用追加模式:
new FileOutputStream(localFile, true),这样不会覆盖已有的下载内容。
- 打开文件输出流时,使用追加模式:
保存进度与异常处理:
- 每下载一定量的字节(比如每1MB)就更新一次进度文件;
- 捕获网络中断、IO异常等情况,在异常发生时及时保存当前进度,避免进度丢失。
完整代码示例
import java.io.*; import java.net.HttpURLConnection; import java.net.URL; public class ZipResumeDownloader { // 替换成你的下载链接 private static final String DOWNLOAD_URL = "http://your-server.com/target-file.zip"; // 本地保存路径 private static final String LOCAL_ZIP_PATH = "/your/local/path/target-file.zip"; // 进度记录文件 private static final String PROGRESS_FILE = LOCAL_ZIP_PATH + ".download-progress"; public static void main(String[] args) { File zipFile = new File(LOCAL_ZIP_PATH); File progressFile = new File(PROGRESS_FILE); long downloadedBytes = 0; // 读取已下载进度 if (progressFile.exists()) { try (BufferedReader progressReader = new BufferedReader(new FileReader(progressFile))) { downloadedBytes = Long.parseLong(progressReader.readLine()); // 校验本地文件大小和进度记录是否一致,防止文件被篡改 if (zipFile.exists() && zipFile.length() != downloadedBytes) { downloadedBytes = zipFile.length(); updateProgress(downloadedBytes, progressFile); } } catch (IOException e) { System.err.println("读取进度文件失败,将从头开始下载"); downloadedBytes = 0; } } else if (zipFile.exists()) { // 没有进度文件但有已下载的文件,用文件长度作为进度 downloadedBytes = zipFile.length(); try { updateProgress(downloadedBytes, progressFile); } catch (IOException e) { e.printStackTrace(); } } try { URL url = new URL(DOWNLOAD_URL); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // 设置Range请求头,请求从已下载位置开始的剩余内容 connection.setRequestProperty("Range", "bytes=" + downloadedBytes + "-"); connection.connect(); int responseCode = connection.getResponseCode(); switch (responseCode) { case HttpURLConnection.HTTP_PARTIAL: // 服务器支持断点续传,继续下载 String contentRange = connection.getHeaderField("Content-Range"); long totalFileSize = Long.parseLong(contentRange.split("/")[1]); System.out.println("继续下载:已完成 " + downloadedBytes + " / " + totalFileSize + " 字节"); try (InputStream inputStream = connection.getInputStream(); OutputStream outputStream = new FileOutputStream(zipFile, true)) { byte[] buffer = new byte[4096]; int bytesRead; long currentProgress = downloadedBytes; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); currentProgress += bytesRead; // 每下载1MB更新一次进度 if (currentProgress - downloadedBytes >= 1024 * 1024) { updateProgress(currentProgress, progressFile); downloadedBytes = currentProgress; System.out.println("当前进度:" + currentProgress + " / " + totalFileSize + " 字节"); } } // 下载完成,删除进度文件 progressFile.delete(); System.out.println("文件下载完成!"); } break; case HttpURLConnection.HTTP_OK: // 服务器不支持断点续传,从头下载 System.out.println("服务器不支持断点续传,将从头开始下载"); try (InputStream inputStream = connection.getInputStream(); OutputStream outputStream = new FileOutputStream(zipFile)) { byte[] buffer = new byte[4096]; int bytesRead; long currentProgress = 0; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); currentProgress += bytesRead; if (currentProgress - downloadedBytes >= 1024 * 1024) { updateProgress(currentProgress, progressFile); downloadedBytes = currentProgress; System.out.println("当前进度:" + currentProgress + " 字节"); } } progressFile.delete(); System.out.println("文件下载完成!"); } break; case HttpURLConnection.HTTP_REQUESTED_RANGE_NOT_SATISFIABLE: // 请求范围无效,已下载的字节超过了文件总大小,重新下载 System.out.println("已下载内容超过服务器文件大小,将重新下载"); zipFile.delete(); progressFile.delete(); main(args); // 重新启动下载 break; default: System.err.println("下载失败,响应码:" + responseCode); } connection.disconnect(); } catch (IOException e) { System.out.println("下载中断,已保存当前进度"); e.printStackTrace(); // 异常发生时保存进度 try { updateProgress(downloadedBytes, progressFile); } catch (IOException ex) { ex.printStackTrace(); } } } // 更新进度记录文件 private static void updateProgress(long progress, File progressFile) throws IOException { try (BufferedWriter writer = new BufferedWriter(new FileWriter(progressFile))) { writer.write(String.valueOf(progress)); } } }
关键注意点
- 服务器兼容性:不是所有服务器都支持Range请求,所以代码里一定要处理
200 OK的情况,避免直接报错。 - 进度可靠性:用单独的进度文件记录下载进度,同时校验本地文件大小和进度的一致性,防止用户手动修改文件导致的异常。
- 文件完整性:下载完成后,可以对比本地文件大小和服务器返回的总大小,确保文件下载完整。如果服务器提供MD5/SHA哈希值,最好额外校验哈希值,保证文件没有损坏。
- 异常场景处理:网络波动、应用崩溃等情况都要捕获,及时保存进度,避免之前的下载功亏一篑。
内容的提问来源于stack exchange,提问作者Latha
相关产品推荐
相关产品推荐

