Java 断点下载(断点续传)服务端及客户端(Android)代码

Android 断点续传实现原理 下载原理 在介绍断点续传之前,我们先说说下载的原理。代码示例用 OkHttp 作为示例。 下载核心思路是把 responseBody 写入文件,核心代码如下: 但是这种做法有个明显的问题,假如手机在下载文件的时候下载了80%,某些原因断网了,如果不支持断点续传,那就只有被迫重头开始下载。但是如果有断点续传的加持,就只需要下载最后 20% 的资源,避免重新下载。 断点续传原理 1、java.io.RandomAccessFile 断点续传/下载需要使用到 java.io.RandomAccessFile 类 阅读详情

最近在研究断点下载(下载续传)的功能,此功能需要服务端和客户端进行对接编写,本篇也是记录一下关于贴上关于实现服务端(Spring Boot)与客户端(Android)是如何实现下载续传功能

断点下载功能(下载续传)解释:

客户端由于突然性网络中断等原因,导致的下载失败,这个时候重新下载,可以继续从上次的地方进行下载,而不是重新下载

原理

首先,我们先说明了断点续传的功能,实际上的原理比较简单

客户端和服务端规定好一个规则,客户端传递一个参数,告知服务端需要数据从何处开始传输,服务端接收到参数进行处理,之后文件读写流从指定位置开始传输给客户端

实际上,上述的参数,在http协议中已经有规范,参数名为Range

而对于服务端来说,只要处理好Range请求头参数,即可实现下载续传的功能

我们来看下Range请求头数据格式如下:

格式如下:

Range:bytes=300-800
//客户端需要文件300-800字节范围的数据(即500B数据)

Range:bytes=300-
//客户端需要文件300字节之后的数据

我们根据上面的格式,服务端对Range字段进行处理(String字符串数据处理),在流中返回指定的数据大小即可

那么,如何让流返回指定的数据大小或从指定位置开始传输数据呢?

这里,Java提供了RandomAccessFile类,通过seekTo()方法,可以让我们将流设置从指定位置开始读取或写入数据

这里读取和写入数据,我是采用的Java7之后新增的NIO的Channel进行流的写入(当然,用传统的文件IO流(BIO)也可以)

这里,我所说的客户端是指的Android客户端,由于App开发也是基于Java,所以也是可以使用RandomAccessFile这个类

对于客户端来说,有以下逻辑:

先读取本地已下载文件的大小,然后请求下载数据将文件大小的数据作为请求头的数值传到服务端,之后也是利用RandomAccessFile移动到文件的指定位置开始写入数据即可

扩展-大文件快速下载思路

利用上面的思路,我们还可以可以得到一个大文件快速下载的思路:

如,一份文件,大小为2000B(这个大小可以通过网络请求,从返回数据的请求头content-length获取获取)

客户端拿回到文件的总大小,根据调优算法,将平分成合适的N份,通过线程池,来下载这个N个单文件

在下载完毕之后,将N个文件按照顺序合并成单个文件即可

代码

上面说明了具体的思路,那么下面就是贴出服务端和客户端的代码示例

服务端

服务端是采用的spring boot进行编写

/**
 * 断点下载文件
 *
 * @return
 */
@GetMapping("download")
public void download( HttpServletRequest request, HttpServletResponse response) throws IOException {
    //todo 这里文件按照你的需求调整
    File file = new File("D:\\temp\\测试文件.zip");
    if (!file.exists()) {
        response.setStatus(HttpStatus.NOT_FOUND.value());
        return;
    }
    long fromPos = 0;
    long downloadSize = file.length();

    if (request.getHeader("Range") != null) {
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        String[] ary = request.getHeader("Range").replaceAll("bytes=", "").split("-");
        fromPos = Long.parseLong(ary[0]);
        downloadSize = (ary.length < 2 ? downloadSize : Long.parseLong(ary[1])) - fromPos;
    }
    //注意下面设置的相关请求头
    response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
    //相当于设置请求头content-length
    response.setContentLengthLong(downloadSize);

    //使用URLEncoder处理中文名(否则会出现乱码)
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
    response.setHeader("Accept-Ranges", "bytes");
    response.setHeader("Content-Range", String.format("bytes %s-%s/%s", fromPos, (fromPos + downloadSize), downloadSize));

    RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
    randomAccessFile.seek(fromPos);

    FileChannel inChannel = randomAccessFile.getChannel();
    WritableByteChannel outChannel = Channels.newChannel(response.getOutputStream());

    try {
        while (downloadSize > 0) {
            long count = inChannel.transferTo(fromPos, downloadSize, outChannel);
            if (count > 0) {
                fromPos += count;
                downloadSize -= count;
            }
        }
        inChannel.close();
        outChannel.close();
        randomAccessFile.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

客户端

Android客户端,是基于Okhttp的网络框架写的,需要先引用依赖

implementation 'com.squareup.okhttp3:okhttp:3.9.0'

下面给出的是封装好的方法(含进度,下载失败和成功回调):

package com.tyky.update.utils;

import com.blankj.utilcode.util.ThreadUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class FileDownloadUtil {

    public static void download(String url, File file, OnDownloadListener listener) {

        //http://10.232.107.44:9060/swan-business/file/download
        // 利用通道完成文件的复制(非直接缓冲区)
        ThreadUtils.getIoPool().submit(new Runnable() {
            @Override
            public void run() {
                try {

                    //续传开始的进度
                    long startSize = 0;
                    if (file.exists()) {
                        startSize = file.length();
                    }
                    OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
                    Request request = new Request.Builder().url(url)
                            .addHeader("Range", "bytes=" + startSize)
                            .get().build();
                    Call call = okHttpClient.newCall(request);
                    Response resp = call.execute();

                    double length = Long.parseLong(resp.header("Content-Length")) * 1.0;
                    InputStream fis = resp.body().byteStream();
                    ReadableByteChannel fisChannel = Channels.newChannel(fis);

                    RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
                    //从上次未完成的位置开始下载
                    randomAccessFile.seek(startSize);
                    FileChannel foschannel = randomAccessFile.getChannel();

                    // 通道没有办法传输数据,必须依赖缓冲区
                    // 分配指定大小的缓冲区
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

                    // 将通道中的数据存入缓冲区中
                    while (fisChannel.read(byteBuffer) != -1) {  // fisChannel 中的数据读到 byteBuffer 缓冲区中
                        byteBuffer.flip();  // 切换成读数据模式
                        // 将缓冲区中的数据写入通道
                        foschannel.write(byteBuffer);

                        final double progress = ((foschannel.size() + startSize) / length);
                        BigDecimal two = new BigDecimal(progress);
                        double result = two.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
                        //计算进度,回调
                        if (listener != null) {
                            listener.onProgress(result);
                        }
                        byteBuffer.clear();  // 清空缓冲区
                    }
                    foschannel.close();
                    fisChannel.close();
                    randomAccessFile.close();

                    if (listener != null) {
                        listener.onSuccess(file);
                    }
                } catch (IOException e) {
                    if (listener != null) {
                        listener.onError(e);
                    }

                }
            }
        });


    }

    public interface OnDownloadListener {
        void onProgress(double progress);

        void onError(Exception e);

        void onSuccess(File outputFile);
    }
}

使用:

FileDownloadUtil.download(downloadUrl, file, new FileDownloadUtil.OnDownloadListener() {
    @Override
    public void onProgress(double progress) {
        KLog.d("下载进度: " + progress);
    }

    @Override
    public void onError(Exception e) {
        KLog.e("下载错误: " + e.getMessage());
    }

    @Override
    public void onSuccess(File outputFile) {
        KLog.d("下载成功");
    }
});
Android 断点续传,简单易用,快速实现断点续传功能,Android文件下载、安卓文件下载、安卓断点续传、阿里云OSS断点续传、AliyunOSS、Apk更新、Weex更新、Android动态更新 Android断点续传文件下载,简单实用的代码,如需要可参考。 希望不要觉得我又造一个轮子,我先用了网上代码觉得不好使用,而且发现有错误; 有一些功能强大的框架,我懒得去了解,去学习怎样使用它也要花时间; 简单实现自己的功能就好,我比较倾向于容易理解,简单实用。 本人老程序员一个(约20年),没有专注和擅长哪一个开发环境,都是看需要而定; 最近要开发一个自助售卖机的客户端, 今天要实现文件下载功能,用于更新设备上的广告(mp4视频文件), 还有用于版本升级,下载APK文件和Weex打包的. 阅读详情

相关推荐

Spring Boot 实现文件断点下载

但是当文件较大或网络不稳定时,完整下载整个文件可能会耗费较长的时间,并且容易中断。为了解决这个问题,可以使用文件断点下载的方式,允许用户可以从中断的地方继续下载文件,提高下载的效率和稳定性。否则,即使在后端实现了文件断点下载,也无法在客户端实现断点续传的效果。在本文中,我们将介绍如何使用 Spring Boot 实现文件断点下载功能,并提供相应的源代码。首先,我们获取文件的大小,并设置文件下载的响应头信息,包括文件的类型、大小和下载的文件名。类来读取文件,并设置文件指针的位置为起始位置。

AnHaskell的博客 586

Java实战:Spring Boot 实现文件断点下载

本文将详细介绍如何在 Spring Boot 应用程序中实现文件断点下载功能。我们将深入探讨 HTTP 协议中的 Range 请求头以及如何使用 Spring Boot 中的 `ResponseEntity` 和 `HttpHeaders` 类来支持断点下载。

oandy0的博客 2782

Android平台断点续传下载实现指南

断点下载是一种高效且实用的文件传输技术,它允许用户在文件下载过程中遇到问题(如网络中断、设备关机等)时,重新启动下载任务,而不是从头开始。这种方法极大地提高了下载的稳定性和用户体验。在本章中,我们将探讨断点下载的基本概念,包括它的定义、工作原理以及在现代IT应用中的重要性。此外,我们还将简要介绍断点下载与传统下载方式的对比,以及它在移动应用、网页服务等领域中的应用场景。FileUtils类中提供的文件读写操作涉及到了文件的复制、移动、删除和重命名等常用功能。

weixin_31715353的博客 1092

Spring Boot 实现文件断点下载,实战来了!

前言互联网的连接速度慢且不稳定,有可能由于网络故障导致断开连接。在客户端下载一个大对象时,因网络断开导致上传下载失败的概率就会变得不可忽视。客户端在GET对象请求时通过设置Range头部来告诉接口服务需要从什么位置开始输出对象的数据。判断是否支持断点下载,根据文档:14.35.1 Byte Rangeshttps://www.w3.org/Protocols/rfc2616/rfc2616-sec...

架构文摘 735

Android网络多线程断点续传下载实战教程

在了解多线程下载之前,我们先来了解单线程下载的基本原理。单线程下载是指用户设备上的下载客户端与服务器之间建立一个HTTP连接,通过这个连接从服务器下载数据,直到文件下载完成。然而,这种方式受到网络带宽和服务器处理能力的限制,下载速度有限。

weixin_31185473的博客 888

Springboot 断点续传大文件

本人测试上传3G多的文件,未发现问题,但是还有优化空间。 原理 曾经上传过:前端通过计算,拿到整个文件的md5,请求后台是否曾经已经上传过,如果曾经上传过,直接返回成功 曾经没有上传过:后台返回前端,表示从未上传过此文件,前端通过技术,将大文件分隔成无数个小文件,一一上传 曾经上传一部分:前端在上传每个模块之前,先请求后台,判断此模块是否已经上传过,如果已经上传过,则不需要再上传,否则上传。 注:启动以后直接访问http://localhost:9002/demo/largeUpload/open,如果本地运行需要修改maven私服地址 ,使用idea首先需要安装Lombok插件,压缩包包含数据库表脚本文件,数据库名称需要自己创建,使用mysql demo测试网站: http://118.89.41.69:9002/demo/largeUpload/open

Android-Service系列之断点续传下载

PS:本文代码来自慕课网同名课程Android-Service系列之断点续传下载,这里只是为了Mark下来方便自己以后使用。 首先是布局文件,因为只是demo,所以使用图形化工具生成。 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schema

小小桐的专栏 1709

Doris Streamloader安装教程

Doris Streamloader 是一款用于将数据导入 Doris 数据库的专用客户端工具。本文介绍其在centos7环境下的安装过程

qq_44540985的博客 1089

Android实现断点续传(附带源码)

Android实现断点续传(附带源码)

欢迎来到我的博客!这里是一个专注于计算机技术的分享平台,涵盖编程开发、算法研究、系统架构、软件工程等多个领域。无论你是初学者还是资深开发者,都能在这里找到有价值的内容。 1122

从仿真到实机:四足机器人控制算法的全流程验证框架

本文系统阐述了四足机器人从仿真到实机的全流程验证框架。针对仿真与实机环境的巨大差异,文章详细介绍了在MuJoCo等高保真仿真环境中的建模、算法迭代与压力测试方法,并重点解析了通过硬件抽象层和中间件搭建通往实机的关键桥梁。最后,提供了实机部署的安全清单与调试策略,旨在帮助开发者构建可重复、风险可控的算法验证闭环,提升算法在真实世界中的鲁棒性与可靠性。

lilh34434的博客 381

DDR5内存ODT模式全解析:5种状态与实战配置指南(附时序图)

本文深入解析DDR5内存的片上终端电阻(ODT)机制,详细阐述了其五种核心状态(终结电阻禁用、RTT_WR、RTT_NOM_RD、RTT_NOM_WR、RTT_PARK)的定义、切换时序与工作原理。通过解读JESD79-5规范中的时序图,并提供基于模式寄存器的实战配置指南与代码示例,帮助硬件工程师和系统开发者优化信号完整性,解决高负载下的稳定性问题,并实现性能与功耗的最佳平衡。

weixin_29221753的博客 561

java ftp断点续传_Android FTP 多线程断点续传下载\上传的实例

最近在给我的开源下载框架Aria增加FTP断点续传下载和上传功能,在此过程中,爬了FTP的不少坑,终于将功能实现了,在此把一些核心功能点记录下载。FTP下载原理FTP单线程断点续传FTP和传统的HTTP协议有所不同,由于FTP没有所谓的头文件,因此我们不能像HTTP那样通过设置header向服务器指定下载区间。但是FTP协议提供了一个更好用的命令REST用于从指定位置恢复任务,同时FTP协议也提...

weixin_42352351的博客 409

gerrit 配置 ----Gitweb集成

此外,为了让用户能够查看任何关于 gitweb 的信息 项目中,用户必须能够阅读所有引用(包括 refs/meta/config,refs/meta/dashboards/*等)。或者,如果Gerrit通过反向代理送达,它可以 为gitweb的链接生成不同的URL(需要的) 在网页服务器上重写为)。Gerrit 代码审查可以管理和生成指向 gitweb 的超链接, 允许用户从Gerrit内容跳转到相同信息, 但由 gitweb 显示。默认为,这是 Linux 发行版上“gitweb”包的常见安装路径。

QuanQuanYuanXin的博客 1404

Android实现多线程断点续传

前言: 项目都快交付阶段了,客户说要改个需求,添加一个断点

哈喽沃德 1625

用Python+NumPy手把手实现分数阶傅里叶变换(FRFT),搞定线性调频信号分析

本文详细介绍了如何使用Python和NumPy实现分数阶傅里叶变换(FRFT),特别针对线性调频信号(Chirp信号)分析。通过从零开始的代码实现和可视化案例,展示了FRFT在时变信号处理中的强大能力,帮助工程师和研究人员高效分析频率随时间变化的信号。

weixin_29062823的博客 217

Geogebra中级篇016—矩阵(Matrices)

在GeoGebra中,矩阵是一种强大的工具,广泛应用于线性代数、几何变换和数据处理等领域。通过矩阵,我们可以轻松实现复杂的计算和图形变换,如旋转、平移和缩放等操作。不仅如此,GeoGebra的矩阵功能还支持多种运算,如加法、乘法、求逆等,使得用户能够高效地处理线性方程组和向量空间问题。在本文中,我们将深入探讨GeoGebra中的矩阵功能,详细介绍如何创建矩阵、执行常见的矩阵运算,并通过实例展示这些操作在数学和工程应用中的实际价值。这篇文章将为你提供关于GeoGebra矩阵操作的全面指南。

八荒启-交互动画的博客 2579

结合服务端实现断点续传

本人第一次写博客,才疏学浅,希望大牛能指出不足,如有错,请勿喷 什么是断点续传?     断点续传是指,支持从文件上次中断的地方开始传送数据,而并非是从文件开头传送。这样做的优点是,如果在传输一个比较大的文件,发生连接超时错误时,只要客户端记录下已经接受的文件的大小,下次连接时,告诉服务器上次接受了多少,服务器就能跳过你已经接受部分继续传输,而你客户端只要将其追加在上次保存文件位置之后

一个有梦想的码农的博客 3102

基于STM32+华为云设计的智能鱼缸

本项目基于STM32单片机设计一款基于物联网的智能鱼缸系统,并通过应用侧接口开发上位机APP实现远程对鱼缸参数的检测和控制。该系统包括水温检测、水质检测、自动或手动换水、氛围灯灯光变换和自动或手动喂食等功能,可以帮助用户更好地管理鱼缸,提高鱼儿的生存率,同时增强用户对家庭装饰品的掌控感和科技感。

1万+

Android网络编程 --断点续传下载文件

Android网络编程 --断点续传下载文件

gfjggtf的博客 546

17、非线性动力学特征在情感语音与食管语音分析中的应用

本文探讨了非线性动力学特征在情感语音与食管语音分析中的应用。在情感语音识别中,选取Hurst指数、相关维数和Lempel-Ziv复杂度等混沌特征结合统计量,通过神经网络实现了中性、恐惧和愤怒情感的高效分类,全局成功率高达93.78%。在食管语音分析中,针对其缺乏明显周期性的问题,提出基于改进自相关方法的基频测量流程,包括包络计算、平滑与峰值细化,并对基频的物理意义提出思考。研究展示了两类语音在特征表现与技术方法上的差异,展望了其在客服、安全、医疗康复等领域的应用前景,并指出了未来在特征拓展、算法优化和发声机

sunny的博客 71

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

android断点续传原理,Android-断点续传

简介其实断点续传的原理很简单,从字面上理解,所谓断点续传就是从停止的地方重新 下载。 断点:线程停止的位置。 续传:从停止的位置重新下载。用代码解析就是: 断点 : 当前线程已经下载完成的数据长度。 续传 : 向服务器 请求上次线程停止位置之后的数据。 原理知道了,功能实现起来也简单。每当线程 停止时就把已下载的数据长度写入记录文件,当重新下载时,从记录文件读取已经 下载了的长度。而这个长度就是所...

weixin_29041195的博客 481

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

(安卓) 断点续传

Range : 用于客户端到服务器端的请求,可通过该字段指定下载文件的某一段大小,及其单位。典型的格式如: Range: bytes=0-499 下载第0-499字节范围的内容 Range: bytes=500-999  下载第500-999字节范围的内容 Range: bytes=-500  下载最后500字节的内容 Range: bytes=500-  下载从第500字节开始到

下午茶的博客 745

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

浅析Android应用开发中的断点续传和下载

浅析Android应用开发中的断点续传和下载1、断点续传的原理   要实现断点续传的功能,通常都需要客户端记录下当前的下载进度,并在需要续传的时候通知服务端本次需要下载的内容片段,因此需要实时更新数据库中已经下载的部分内容并记录断点位置。断点续传是HTTP 1.1协议的一部分,简单的来说,只要使用了HTTP协议中的如下字段来和服务器端交互,就可以实现文件下载的断点续传:Range : 用于客户端到...

qq_41453257的博客 430
上一篇: Spring AOP +redisson的redis分布式锁实现
下一篇: JAVA 简单缓存实现-nacos
shadow_zed
博客等级 码龄10年 6289粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值