首页 > 编程语言 >HTTP文件断点续传原理解析(源码)

HTTP文件断点续传原理解析(源码)

时间:2023-10-18 13:45:34浏览次数:32  
标签:raf 断点续传 HTTP 文件 mFileInfo 源码 downloadInfo progress 下载

生活中,有许多事物,在没有被揭开面纱之前,我们往往会觉得很神秘很高深,认为它一定很难,进而望而却步,失去了解它的机会。然而,很多事,只要我们自己能沉下心来,细细研究,那些神秘高深的,也会变得简单明了。"HTTP文件断点续传"就是这样一个好例子,深入了解背后之理,“HTTP文件断点续传原理”其实很简单。

一、什么是断点续传
1.定义:

可以从下载或上传断开点继续开始传输,就叫断点续传。

2.核心实现原理:

i.RandomAccessFile(文件任意位置保存)
方法seek():可以移动到保存文件任意位置,在该位置发生下一个读取或写入操作

ii.HttpURLConnection.setRequestProperty()(任意位置请求返回剩余文件)

HttpURLConnection.setRequestProperty(“Range”, “bytes=” + start + “-” + end)

二、实例分析
流程图

 

 

实现步骤

1.建立数据库:保存文件下载信息
2.下载服务类(DownloadService)
3.两个线程:文件信息线程(FileInfoThread)和文件下载线程(DownloadThread)
4.广播(BroadcastReceiver):UI进度更新
1.建立数据库
按常规数据库建立方法,具体(略)。数据保存信息为:

/**
* 下载信息类
*/
public class DownloadInfo {
private int id;
private String url;//下载链接
private long start;//开始大小
private long end;//最终大小
private long progress;//下载进度
}

2.下载服务类
利用service多次启动只调用onStartCommand()方法,处理开始或暂停下载逻辑。

public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(ACTION_START)) {
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra(TAG_FILEINFO);
mFileInfoThread = new FileInfoThread(fileInfo,mHandler);
mFileInfoThread.start();

} else if (intent.getAction().equals(ACTION_PAUSE)) {
if (mDownloadThread != null) {
mDownloadThread.setPause(true);
}
}
return super.onStartCommand(intent, flags, startId);
}

3.两个线程
i.文件信息线程(FileInfoThread)

通过网络获取下载文件大小,并建立对应大小的保存文件路径。

HttpURLConnection conn = null;
RandomAccessFile raf = null;
try {
URL url = new URL(mFileInfo.getUrl());
conn = (HttpURLConnection) url.openConnection();//连接网络文件
conn.setConnectTimeout(3000);
conn.setRequestMethod("GET");
int length = -1;
Log.e(TAG,"HttpResponseCode=="+ conn.getResponseCode() + "");
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
//获取文件长度
length = conn.getContentLength();
}
if (length < 0) {
return;
}
File dir = new File(DownloadService.DOWNLOAD_PATH);
if (!dir.exists()) {
dir.mkdir();
}
//在本地创建文件
File file = new File(dir, mFileInfo.getFileName());
raf = new RandomAccessFile(file, "rwd");
//设置本地文件长度
raf.setLength(length);
mFileInfo.setLength(length);
Log.e(TAG,"下载文件大小(size)"+ mFileInfo.getLength() + "");
mHandler.obtainMessage(DownloadService.MSG_FILEINFO, mFileInfo).sendToTarget();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null && raf != null) {
raf.close();
conn.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}

}


ii.文件下载线程(DownloadThread)
断点续传原理核心类。
1.判断下载进度是否有保存,若无,数据插入一条数据。

if (!mDatabaseOperation.isExists(downloadInfo.getUrl(), downloadInfo.getId())) {
mDatabaseOperation.insert(downloadInfo);
}

2.设置网络请求Range参数,从请求位置返回数据

//设置下载位置
long start = downloadInfo.getStart() + downloadInfo.getProgress();
connection.setRequestProperty("Range", "bytes=" + start + "-" + downloadInfo.getEnd());

3.通过RandomAccessFile从进度保存位置保存文件

RandomAccessFile raf;
File file = new File(DownloadService.DOWNLOAD_PATH, mFileInfo.getFileName());
raf = new RandomAccessFile(file, "rwd");
raf.seek(start);
...
//写入文件
raf.write(buffer, 0, len);

4.用户暂停时,保存下载进度

//下载暂停时,保存进度
if (isPause) {
Log.e(TAG,"保存进度文件(size):"+progress + "");
mDatabaseOperation.update(mFileInfo.getUrl(), mFileInfo.getId(), progress);
return;
}

4.广播(BroadcastReceiver):
每秒广播一次,刷新UI

long time = System.currentTimeMillis();
......
if (System.currentTimeMillis() - time > 1000) {//超过一秒,就刷新UI
time = System.currentTimeMillis();
sendBroadcast(intent,(int)(progress * 100 / mFileInfo.getLength()));
Log.e(TAG,"进度:" + progress * 100 / mFileInfo.getLength() + "%");
}

DownloadThread类源码:

/**
* 文件下载线程
* Created by AwenZeng on 2017/9/6.
*/

public class DownloadThread extends Thread {
private DownloadInfo downloadInfo;
private FileInfo mFileInfo;
private long progress = 0;
private boolean isPause;
private DatabaseOperation mDatabaseOperation;
private Context mContext;
private static final String TAG = "DownloadThread";

public DownloadThread(Context context, DatabaseOperation databaseOperation, DownloadInfo threadInfo, FileInfo fileInfo) {
this.downloadInfo = threadInfo;
mContext = context;
mDatabaseOperation = databaseOperation;
mFileInfo = fileInfo;
}


public void setPause(boolean pause) {
isPause = pause;
}

@Override
public void run() {
if (!mDatabaseOperation.isExists(downloadInfo.getUrl(), downloadInfo.getId())) {
mDatabaseOperation.insert(downloadInfo);
}
HttpURLConnection connection;
RandomAccessFile raf;
InputStream is;
try {
URL url = new URL(downloadInfo.getUrl());
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(3000);
connection.setRequestMethod("GET");
//设置下载位置
long start = downloadInfo.getStart() + downloadInfo.getProgress();
connection.setRequestProperty("Range", "bytes=" + start + "-" + downloadInfo.getEnd());

//设置文件写入位置
File file = new File(DownloadService.DOWNLOAD_PATH, mFileInfo.getFileName());
raf = new RandomAccessFile(file, "rwd");
raf.seek(start);

progress += downloadInfo.getProgress();
Log.e(TAG,"下载文件进度(size):"+ downloadInfo.getProgress() + "");
Log.e(TAG,"HttpResponseCode ==="+connection.getResponseCode() + "");
//开始下载
if (connection.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
Log.e(TAG,"剩余文件(size):"+connection.getContentLength() + "");
Intent intent = new Intent(DownloadService.ACTION_UPDATE);//广播intent
is = connection.getInputStream();
byte[] buffer = new byte[1024 * 4];
int len = -1;
long time = System.currentTimeMillis();
while ((len = is.read(buffer)) != -1) {
//下载暂停时,保存进度
if (isPause) {
Log.e(TAG,"保存进度文件(size):"+progress + "");
mDatabaseOperation.update(mFileInfo.getUrl(), mFileInfo.getId(), progress);
return;
}
//写入文件
raf.write(buffer, 0, len);
//把下载进度发送广播给Activity
progress += len;
if (System.currentTimeMillis() - time > 1000) {//超过一秒,就刷新UI
time = System.currentTimeMillis();
sendBroadcast(intent,(int)(progress * 100 / mFileInfo.getLength()));
Log.e(TAG,"进度:" + progress * 100 / mFileInfo.getLength() + "%");
}
}
sendBroadcast(intent,100);
/**
* 删除下载信息(重新下载)
*/
mDatabaseOperation.delete(mFileInfo.getUrl(), mFileInfo.getId());
is.close();
}
raf.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}

private void sendBroadcast(Intent intent,int progress){
intent.putExtra("progress",progress);
mContext.sendBroadcast(intent);
}
}

 

参考文章:http://blog.ncmem.com/wordpress/2023/10/18/http%e6%96%87%e4%bb%b6%e6%96%ad%e7%82%b9%e7%bb%ad%e4%bc%a0%e5%8e%9f%e7%90%86%e8%a7%a3%e6%9e%90%e6%ba%90%e7%a0%81/

欢迎入群一起讨论

 

 

标签:raf,断点续传,HTTP,文件,mFileInfo,源码,downloadInfo,progress,下载
From: https://www.cnblogs.com/songsu/p/17771879.html

相关文章

  • kubeadm 加入work 节点集群时报 http://localhost:10248/healthz处理方法
    现象:[kubelet-check]TheHTTPcallequalto'curl-sSLhttp://localhost:10248/healthz'failedwitherror:Get"http://localhost:10248/healthz":dialtcp127.0.0.1:10248:connect:connectionrefused.[kubelet-check]Itseemslikethekube......
  • HTTP——断点续传(分块传输)
    HTTP——断点续传(分块传输)断点续传:指的是在上传/下载时,将任务(一个文件或压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传/下载,如果碰到网络故障,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有必要从头开始上传/下载。可以节省时间,提高速度。断点续传......
  • 7×24无人值守直播推流软件开发实战,揭开视频推流的底层原理(附源码)
    一、前言你有看到过那种不间断型的、循环播放视频音乐的直播间吗?或者那种直播播放电影的直播间?还有层出不穷的文章,类似如下标题:“如何搭建一个24小时不间断的直播间?躺入xxxx元!”“24小时电影直播间,每天到账xxx~xxxx,不出镜副业,人人可做!”“50块的云服务器直播推流让我月入过千......
  • 7×24无人值守直播推流软件开发实战,一文为你揭开视频推流的底层原理(附源码)
    一、前言你有看到过那种不间断型的、循环播放视频音乐的直播间吗?或者那种直播播放电影的直播间?还有层出不穷的文章,类似如下标题:“如何搭建一个24小时不间断的直播间?躺入xxxx元!”“24小时电影直播间,每天到账xxx~xxxx,不出镜副业,人人可做!”“50块的云服务器直播推流让我月入过千?......
  • Sentinel源码改造,实现Nacos双向通信!
    SentinelDashboard(控制台)默认情况下,只能将配置规则保存到内存中,这样就会导致SentinelDashboard重启后配置规则丢失的情况,因此我们需要将规则保存到某种数据源中,Sentinel支持的数据源有以下这些:然而,默认情况下,Sentinel和数据源之间的关系是单向数据通讯的,也就是只能先在数......
  • Go - Making an HTTP Client Request
    Problem: YouwanttomakeanHTTPrequesttoawebserver.Solution: Usethenet/httppackagetomakeanHTTPrequest. HTTPisarequest-respondprotocol,andservingrequestsisonlyhalfofthestory.Theotherhalfismakingrequests.Thenet/http......
  • Go - Serving Through HTTPS
    Problem: YouwanttoserveyourwebapplicationthroughHTTPS.Solution: Usethehttp.ListenAndServeTLSfunctiontoserveyourwebapplicationthroughHTTPS. HTTPSisnothingmorethanlayeringHTTPontopoftheTransportSecurityLayer(TLS).Thenet......
  • SpringBoot启动流程源码分析(2)
    1、启动引导类大部分时候,SpringBoot应用主要通过在引导类调用SpringApplication的静态run方法启动,同时将引导类注册为配置源。比如下面是一个SpringMVC的Web应用,引导类是WebMVCBootstrap,和命令行参数args作为SpringApplication静态run方法的参数,用于构建SpringApplication对象和......
  • ctypes学习 + GearDVFS源码分析(/src/perf_lib)
      最近在尝试复现GearDVFS的代码,代码结构很复杂,由于需要获取硬件信息,作者使用ctypes实现与底层的交互,任务紧迫,开始学习吧!1.ctypes介绍  资料的来源已经放在了后文的链接中,由于我的基础实在很薄弱,因此看了很多资料才搞懂ctypes的实现原理,如果有和我一样的菜鸟,在学习之前可以......
  • Spring源码分析系列——循环依赖解析(附详尽流程图)
    前言本文分析spring循环依赖,我们知道构造函数填充属性是天然无法解决循环依赖的,而且解决循环依赖必须至少需要一个单例bean提前暴露。用xml标签配置属性bean,和@autowire注解注入属性bean,注入属性过程是不一样的。(1)xml标签配置属性bean是在解析xml过程中直接将属性值填充到be......