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

2023-11-19

生活中,有许多事物,在没有被揭开面纱之前,我们往往会觉得很神秘很高深,认为它一定很难,进而望而却步,失去了解它的机会。然而,很多事,只要我们自己能沉下心来,细细研究,那些神秘高深的,也会变得简单明了。"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);
    }
}

三、源码地址

如果你觉得还不错,欢迎star或fork。
https://github.com/awenzeng/BreakPointDemo

四、参考文献

RandomAccessFiley详解
http断点续传原理:http头 Range、Content-Range
InputStream中read()与read(byte[] b)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

HTTP文件断点续传原理解析(源码) 的相关文章

  • 【Android】线性布局(LinearLayout)最全解析

    Android 线性布局 LinearLayout 最全解析 一 LinearLayout概述 二 LinearLayout常用属性 2 1 orientation属性 2 2 gravity属性 2 3 layout weight属性 一
  • Android 对data/data/(your packagename)目录下的数据读写、删除操作

    一 数据存储 App自身的数据存储在 data data packagename 目录下 大致结构如下图 Activity提供了getCacheDir 和getFilesDir 方法 getCacheDir getAbsolutePath
  • Kotlin1.8新特性

    Kotlin1 8 0新特性 新特性概述 JVM 的新实验性功能 递归复制或删除目录内容 提升了 kotlin reflect 性能 新的 Xdebug 编译器选项 提供更出色的调试体验 kotlin stdlib jdk7 与 kotli
  • Android file类使用详解

    一 Android file类 在开发Android应用时免不了会跟文件打交道 本篇文章记录总结自己常用到的文件操作 数据的存储有多种方式 比如数据库存储 SharedPreferences存储 文件存储等 这里我们将要介绍最简单的文件存储
  • ViewPager 详解(二)---详解四大函数

    http blog csdn net harvic880925 article details 38487149
  • 耗时操作ANR和handler

    耗时操作 1 什么是ANR 在应用程序的主线程中执行一段耗时的代码 就有可能出现ANR异常 耗时的代码未执行结束时 界面会卡住 用户对界面进行了操作 10秒之后耗时代码如果还未结束 就会出现ANR异常 2 怎么避免ANR 主线程中不要执行耗
  • Android开发基础 -- android studio 使用第三方模拟器连接方法,如海马玩模拟器

    安装完模拟器后 要使用adb命令Android studio才能识别出来 打开cmd 输入 adb connect 127 0 0 1 26944 如下 海马玩模拟器的端口号是26944 逍遥安卓模拟器的端口号是21503 夜神玩模拟器的端
  • Android(java方法)上实现mp4的分割和拼接 (二)

    http blog csdn net banking17173 article details 20646251 这节谈一下如何在Android上实现mp4文件的高效率切割 业务需求举例 把一段2分钟的mp4文件切割出00 42 至 01
  • Android中定时执行任务的3种实现方法

    在Android开发中 定时执行任务的3种实现方法 一 采用Handler与线程的sleep long 方法 不建议使用 java的实现方式 二 采用Handler的postDelayed Runnable long 方法 最简单的andr
  • Android开发——菜单(Menu)-——选项菜单(OptionMenu)

    Menu 在Android3 0以前的menu显示 是用户点击手机下方操作按钮的菜单按钮时 会从界面底部向上弹出菜单 菜单内容出现在屏幕底部 可以包含六个及以上的菜单项 超出的部分则以 更多 来显示 在Android3 0以后的更高版本的系
  • 『Android Studio』用Fragment实现一个简易新闻浏览界面

    Fragment意思为碎片 片段 在Android中有些Activity在手机上看起来很美观 但放在屏幕更大的平板类的设备上 可能就不一样了 而Fragment能在一个Activity中内嵌多个独立的小Activity 有效的解决了app在
  • Layout的放大和缩小效果例子(ScaleAnimation)

    个Layout从中心放大和缩小的例子 直接上代码 1 ScaleDialog java文件 Java代码 package cn com import android app Activity import android graphics
  • 安卓知识点-动态权限(A6,A13)

    1 清单文件AndroidManifest xml声明
  • Android基础学习(十七)—— Retrofit

    Retrofit本身并没有提供网络访问的能力 但是它底层封装了OkHttp 也是由Square公司贡献的一个处理网络请求的开源项目 A type safe HTTP client for Android and Java https git
  • C/C++在Android开发中的应用

    JNI开发系列阅读 JNI与底层调用1 http blog csdn net axi295309066 article details 60758515 JNI与底层调用2 http blog csdn net axi295309066 a
  • Android Socket 简单介绍

    文章目录 前言 一 Socket是什么 百度百科的解释 我自己的理解 二 简单示例 1 服务端 2 客户端 3 布局 4 实现 参考 总结 前言 最近需求需要使用Socket进行通讯 我在工作后的安卓开发中没有接触过 所以有了这篇文章 写的
  • Android四大组件-BroadcastReceiver、ContentProvider、Service

    目录 一 BroadcastReceiver 1 定义 2 作用 3 生命周期 4 广播注册方式 动态注册 静态注册 5 广播类型 普通广播 即发出广播后所有接收者都能收 有序广播 按照广播的优先级接受 broadcastReceiver可
  • ndk错误总结

    1 ndk Unresolved inclusion
  • Gradle入门(二)尝试理解gralde编译项目

    前言 前面我们了解了如何通过groovy DSL转换为KTS 我也在尝试的证明可以看到源码和有代码提示对于入门的重要性 2022年11月12日 我发现最新的idea 有gradle的代码提示 点击也可以看到源码 学习Gradle还是建议整一
  • 多线程下载文件(支持暂停、取消、断点续传)

    多线程下载文件 支持暂停 取消 断点续传 多线程同时下载文件即 在同一时间内通过多个线程对同一个请求地址发起多个请求 将需要下载的数据分割成多个部分 同时下载 每个线程只负责下载其中的一部分 最后将每一个线程下载的部分组装起来即可 涉及的知

随机推荐

  • 解决:ln: failed to create symbolic link ‘/usr/bin/java’: File exists

    在centos安装java环境 增加软链接时报 ln failed to create symbolic link usr bin java File exists root iZbp12f9404um3f6avsm29Z ln s usr
  • mapbox中对同一个图层layer,设置不同颜色要素

    同一图层layer的不同要素feature设置不同的颜色 InitLayer 构造函数 mapbox初始化地图 function InitLayer map this map map this jsonPoints type Feature
  • PE文件结构详解精华(从头看下去就能大概了解PE文件结构了)

    前言 本博客系统讲解了PE文件结构 PE文件结构其实不复杂 但内容较多 希望朋友们能 沉心静气做学问 目录 前言 1 PE文件及其表现形式 span span 2 PE文件格式与恶意软件的关系 span span 3 PE文件格式的总体结构
  • .gitignore不生效问题

    问题 在使用git进行版本控制的过程中发现 将想被忽略的文件 文件夹 配置到 gitignore文件中后 实际修改了想被忽略的文件 调用git status查看时 仍然会提示提交这些文件 也就是说实际并没有被忽略 原因 原因是git ign
  • dxva2+ffmpeg硬件解码(Windows)重要笔记2

    参考了csdn上Win32Project1 ffmpeg dxva2这个例子 很不错 直接就可以运行 但是 有几个问题 1 窗口无法正常缩放 缩放后 图像大小并没有一起缩放 2 H265的编码格式 显示下面有一块绿色 3 无法从显卡获取YU
  • Python(符号计算常微分方程)谐振子牛顿运动方程

    牛顿运动方程 牛顿运动方程可以写成以下形式 F d p d t
  • 图像jpg转.bgr二进制文件_海思开发

    作者 昌山小屋 来源 CSDN 原文 https blog csdn net ChuiGeDaQiQiu article details 84945901 版权声明 本文为博主原创文章 转载请附上博文链接 参考为毛我要干这件事 把一张jpg
  • R语言练习题答案(9)第六章高级绘图实例代码

    关注公众号凡花花的小窝 含有更多更全面的计算机专业编程考研相关知识的文章还有资料 代码 6 1 散点图 library lattice xyplot wt 3 5407 mpg data mtcars ylab Weight xlab Ki
  • 计算机仿真应用于诊断什么故障,基于MATLAB/Simulink的机械故障诊断研究

    摘要 机械故障诊断技术能够提供高质量的监控系统 提升管理效率 降低维护成本 通过MATLAB Simulink仿真技术可以简洁地将故障诊断的结果图像化表达出来 提高故障诊断的质量和效率 该文模拟了机械故障诊断的全过程 充分发挥技术融合的优势
  • 【路由交换实验】生成树STP(stp,rstp,mstp)

    理论 冗余的重要性 叫做MAC地址表的震荡 为了增加网络的可靠性 有必要引入双链路的备份 但是引入冗余链路以后 网络里面又产生了环路 产生一系列广播风暴的问题 可以使用STP生成树协议来解决这个问题 生成树协议可以在提高可靠性的同时 又能避
  • python下载后安装包在哪里找到_python安装包里idle在哪

    进入python安装目录 比如python安装在d盘 你只要进到下面这个文件夹去 D Program Files Python33 Lib idlelib idle bat运行即可 至于出现一个黑色的空白cmd命令提示符窗口的解决方法只要用
  • IDEA社区版下载安装

    一 下载网址 https www jetbrains com idea download 这里以安装社区版免费版为例 二 点击下载好的安装文件 点击 Next 建议更改下路径 点击 Next 点击 Next 可点击创建快捷方式 其他默认就好
  • matlab实现遗传算法——以Ras函数为例( 初学子 友好子 )

    遗传算法 演化思想 遗传算法的本质 遗传算法的本质 模拟生物演化 具体来说 模拟对象是生物演化中的种种自然现象 如变异 交叉互换 交配 淘汰 因此 一个优秀的遗传算法首先应该做到生物演化的模拟 但是并非仅仅对生物演化进行简单复现 生物演化中
  • Mybatis plus 数据加密

    数据加密重中之重个铁铁 先准备加解密工具类 package com byyl web utils import org springframework util Base64Utils import javax crypto Cipher
  • 【密码学】破解维吉尼亚密码(C++代码实现)

    问题简述 维吉尼亚密码是使用一系列凯撒密码组成密码字母表的加密算法 属于多表密码的一种简单形式 在一个凯撒密码中 字母表中的每一字母都会作一定的偏移 例如偏移量为3时 A就转换为了D B转换为了E 而维吉尼亚密码则是由一些偏移量不同的凯撒密
  • unix环境高级编程第三版源代码编译及使用

    估计好多学习unix linux的程序员都知道有这么一本书 最近笔者也开始膜拜膜拜此书 在编译源代码的时候 遇到了一些问题 现在在这里做下总结 加深自己的印象 或许也有遇到此问题的同仁 可以参考参考 先强调一下 笔者的开发环境是centos
  • 单细胞测序数据的降维方法和细胞亚型鉴定聚类方法

    单细胞测序数据的降维方法和细胞亚型鉴定聚类方法是单细胞转录组分析中常用的技术 下面是对这些方法的总结 1 降维方法 主成分分析 PCA PCA是一种常用的降维方法 通过线性变换将高维数据转化为低维表示 保留最大的方差 t SNE t SNE
  • Nginx一键自动化部署安装shell脚本

    bin bash 安装Nginx相关依赖 yum install y gcc make wget zlib devel openssl devel pcre devel 下载Nginx源码 wget http nginx org downl
  • Android中字体使用的单位是,Android中设置TextView字体大小时的单位问题

    项目开发中使用如下代码动态设置TextView字体大小 发现设置的字体和预期差别很大 errText setTextSize context getResources getDimensionPixelSize R dimen lost t
  • HTTP文件断点续传原理解析(源码)

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