【Java技术】基于Http的文件断点续传实现

news2024/11/29 0:37:13

1.断点续传的介绍

客户端软件断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。从而达到让用户节省时间,提高速度的目的。
  
2.断点续传的环境要求

(1). 如果是基于http请求与响应实现的断点续传,需要服务器支持"响应一部分"数据的功能;(本案例采用的是tomcat7服务器,而tomcat7服务器是支持这个功能的)
  
(2). 在客户端需要使用RandomAccessFile类对文件任意位置的数据进行随机读写操作;

3.java的RandomAccessFile类介绍

java的API中对RandomAccessFile类的解释如下:
在这里插入图片描述
我对RandomAccessFile类的理解是:RandomAccessFile类是java提供的一个可以用于随机读写文件内容的类,我们可以对RandomAccessFile类关联的文件中的任意位置和任意大小的数据进行任意的读写操作;因此要想完成文件的断点续传操作,该类的使用是必不可少的!

4.断点续传的基本实现思路
在这里插入图片描述

5.断点续传的代码实现

基础环境搭建:
  
(1). 创建WEB的maven工程;

(2). 引入maven的tomcat7插件;

(3). 在webapp目录下存放多个文件,以备测试断点续传下载使用;
  java客户端代码实现:

public class MyDownLoadClient {
    public static String urlpath = "http://127.0.0.1:80/";
    private static int threadCount = 5;

    public static void main(String[] args) throws Exception {
        // 让用户输入要下载的文件名称
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入要下载的文件名称:");
        String file = sc.next();
        urlpath = urlpath.concat(file);
        // 获取文件总大小
        URL url = new URL(urlpath);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(2000);
        int responseCode = conn.getResponseCode();
        if (responseCode == 200) {
            int contentLength = conn.getContentLength();
            System.out.println("length" + contentLength);
            int part = contentLength / threadCount;
            // 读配置文件
            ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
            CountDownLatch count;
            InputStream in = MyDownLoadClient.class.getClassLoader().getResourceAsStream(file + ".properties");
            if (in != null) {
                // 说明该文件不是第一次下载,需要断点续传
                Properties p = new Properties();
                p.load(in);
                in.close();
                Set<String> keys = p.stringPropertyNames();
                count = new CountDownLatch(keys.size());
                for (String key : keys) {
                    String value = p.getProperty(key);
                    String[] arr = value.split(",");
                    long start = Long.parseLong(arr[0]);
                    long end = Long.parseLong(arr[1]);
                    map.put(key,value);
                    new DownloadThread(start, end, key, map, count, file).start();
                }
                p.clear();
                p = null;
            } else {
                count = new CountDownLatch(threadCount);
                // 说明该文件是第一次下载,直接下载即可
                for (int i = 0; i < threadCount; i++) {
                    long startIndex = i * part; //每个线程起始下载位置

                    long endIndex = (i + 1) * part;//每个线程的结束位置

                    if (i == threadCount - 1) {//最后一个线程的结束位置

                        endIndex = contentLength;

                    }
                    map.put( String.valueOf(i),startIndex+","+endIndex);
                    new DownloadThread(startIndex, endIndex, String.valueOf(i), map, count, file).start();
                }
            }
            // 等待任务完成,删除配置文件
            count.await();

            new File(MyDownLoadClient.class.getClassLoader().getResource("").getPath(),file + ".properties").delete();
            System.out.println("==========================下载任务完成==========================");
        } else {
            System.out.println("连接服务器失败...请检查服务器是否畅通及资源路径是否正确...");
        }
    }
}

下载任务的线程代码实现:

class DownloadThread extends Thread {
    private long startIndex;
    private long endIndex;
    private String threadId;
    private ConcurrentHashMap<String, String> map;
    private CountDownLatch count;
    //private long subTotal = 0;
    private String fileName;

    public DownloadThread(long startIndex, long endIndex, String threadId, ConcurrentHashMap<String, String> map, CountDownLatch count, String fileName) {
        this.startIndex = startIndex;
        this.endIndex = endIndex;
        this.threadId = threadId;
        this.map = map;
        this.count = count;
        this.fileName = fileName;
    }

    @Override
    public void run() {
        try {
            URL url = new URL(MyDownLoadClient.urlpath);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setRequestMethod("GET");

            conn.setConnectTimeout(5000);
            //固定写法,表示向服务器请求部分资源
            conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);

            int responseCode = conn.getResponseCode();
            //状态码206表示请求部分资源成功
            if (responseCode == 206) {
                RandomAccessFile rafAccessFile = new RandomAccessFile(fileName, "rw");
                rafAccessFile.seek(startIndex);
                InputStream is = conn.getInputStream();
                int len = -1;
                byte[] buffer = new byte[1024];
                Random r = new Random();
                while ((len = is.read(buffer)) != -1) {
                    FileOutputStream fout = new FileOutputStream(this.getClass().getClassLoader().getResource("").getPath()+"\\"+fileName + ".properties");
                    try {
                        //模拟意外情况导致下载中断的代码

                        /*if (r.nextInt(2) == 0) {
                            int i = 1 / 0;
                        }*/
                        rafAccessFile.write(buffer, 0, len);
                        startIndex += len;
                        map.put(threadId, startIndex + "," + endIndex);
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException();
                    } finally {
                        Set<Map.Entry<String, String>> entries = map.entrySet();
                        for (Map.Entry<String, String> entry : entries) {
                            fout.write((entry.getKey() + "=" + entry.getValue() + "\r\n").getBytes());
                        }
                        fout.close();
                    }
                }
                rafAccessFile.close();
                System.out.println("线程" + threadId + "下载完成");
                System.gc();
            }
            count.countDown();
        } catch (Exception e) {
            e.printStackTrace();
            System.gc();
        }
    }
}

6:功能测试

(1). 在web工程中提前准备好要下载的文件;(任意类型,任意文件均可,本项目以三个api举例)

在这里插入图片描述
 (2). 启动tomcat服务器;(已经设置虚拟目录为 “/” 端口号为 “80”)
 在这里插入图片描述
(3). 启动java主程序类(MyDownLoadClient),输入要下载的文件名;
在这里插入图片描述
(4). 可以通过打开线程任务中模拟意外情况的代码,让下载出现意外,当程序出现意外后,配置文件不会删除,且会记录下所有线程已经完成的下载量,以便于下次执行下载任务的时候,可以在此基础上继续完成下载任务;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  (5). 关闭模拟意外的代码,重新执行程序,直到文件顺利下载完成,程序会自动删除对应的配置文件;

在这里插入图片描述
7.功能实现总结
 
断点续传最核心的思想就是利用RandomAccessFile类将一个大文件配合多线程拆分成多个片段进行读写,最终将多个线程读写的结果再合并成1个大文件即可;

8.源代码参考

Java最新课程:

Java零基础视频教程(2022最新Java入门,含斯坦福大学练习题+力扣算法题

Java基础入门:

java零基础自学首Java入门教程(含Java项目和Java真题)

Javaweb核心基础

JavaWeb基础教程,Java web从入门到企业实战完整版

Spring Cloud最全微服务架构

史上最全面的springcloud微服务技术栈

SSM框架教程:

SSM框架教程_Spring+SpringMVC+Maven高级+Spring

SpringBoot2全套视频教程:

SpringBoot2全套视频教程,springboot零基础到项目实战

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/344924.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电子科技大学操作系统期末复习笔记(二):进程与并发控制

目录 前言 进程管理 进程基本知识 程序的顺序执行 前趋图 程序的并发执行 并发程序 进程的定义和特征 进程的特征和状态 操作系统内核 定义 功能 原语 原子操作的实现 操作系统控制结构 进程控制块PCB 进程组织&#xff08;进程树&#xff09; 进程的创建 …

3DMAX高级弯曲插件使用教程

3dMax高级弯曲插件是对3dmax原生“弯曲&#xff08;Bend&#xff09;”修改器的一个增强&#xff0c;给用户更多控制弯曲修改器的参数设置&#xff0c;它让用户输入宽度&#xff0c;插件脚本将移动中心以获得正确的宽度。 主要特性&#xff1a; - 使用智能捕捉捕捉到自定义网格…

静态库和动态库的制作

一、什么叫做库&#xff1a; 库&#xff1a;二进制的程序&#xff0c;能被操作系统载入内存中执行 二、Linux下的库有两种&#xff1a;静态库和共享库(动态库)&#xff0c;二者的不同点在于代码载入的时刻不同。 A、静态库在程序编译的时候并会被连接到目标代码中&#xff0c;程…

SSM项目-商城后台管理系统

SSM项目-商城后台管理系统开发说明开发环境项目界面演示项目功能具体的技术指标开发过程1、搭建SSM框架1.1、建库建表1.2、新建Maven工程1.3、配置pom.xml1.4、目录结构1.5、jdbc.properties1.6、mybatis-config.xml1.7 两个Spring的配置文件applicationContext_dao.xmlapplica…

数据库(三)

第三章 MySQL库表操作 3.1 SQL语句基础 3.1.1 SQL简介 SQL&#xff1a;结构化查询语言(Structured Query Language)&#xff0c;在关系型数据库上执行数据操作、数据检索以及数据维护的标准语言。使用SQL语句&#xff0c;程序员和数据库管理员可以完成如下的任务。 改变数据…

【图解数据结构与算法】数据结构与算法知识点整理 Data Structures and Algorithms

程序数据结构算法 数据结构是可以存储和组织数据的命名位置。 算法是用于解决特定问题的一组步骤。 数据结构是指&#xff1a;一种数据组织、管理和存储的格式&#xff0c;它可以帮助我们实现对数据高效的访问和修改。 数据结构 数据元素 元素之间的结构。 如果说数据结…

什么人合适学习Python

发了几天的Python基础&#xff0c;也认识了一些朋友&#xff0c;忽然有人问起&#xff0c;说为啥学Python&#xff0c;或者说啥人学习Python&#xff0c;作为一个教龄8年从Python一线讲师到Python教学主管的我和大家分享一下个人的看法&#xff0c;还是提前说一下&#xff0c;个…

winapi模拟鼠标按住左键拖动

继前文《windows下通过uiAutomation技术获取ui元素》介绍获取ui元素信息后&#xff0c;还需要对信息进行修改&#xff0c;但是 uiAutomation 技术并未提供可修改的 api&#xff0c;只能另辟他径看看了。 以 camera raw 为例&#xff0c;已知的是可将鼠标放在指定区域位置&#…

电力国家(行业)标准目录

1、3&#xff5e;63kV交流高压负荷开关 GB 3804-90 代替 GB 3804-882、电气装置安装工程35kV及以下架空电力线路施工及验收规范Code for construction and acceptance of 35kVand umder over head power levels electricequipment installation engineeringGB50173—923、带电作…

论文文献引用规范和标准(国标GBT7714)@endnote国标样式@citation交叉引用编号

文章目录论文文献引用规范和标准&#xff08;国标GBT7714&#xff09;国标GBT7714-2015endnote stylerefs简述国标GBT7714条目的组织格式&#x1f388;Noteword中的文献交叉引用超链接[编号]&#x1f388;正则高级搜索批量选中引用序号上标调整更新引用编号项段落内容对齐居中&…

python爬虫学习记录

初识爬虫 爬虫的概念 「什么是爬虫」 爬虫&#xff1a;通过编写程序&#xff0c;模拟浏览器上网&#xff0c;并抓取有价值的数据的过程 反爬虫&#xff1a;门户网站通过制定相应的策略或技术手段&#xff0c;来阻止爬虫程序对其网站数据的爬取 反反爬&#xff1a;爬虫程序…

操作系统(五)页面置换算法与分配策略

操作系统&#xff08;五&#xff09;页面置换算法与分配策略 一、页面置换算法 1、最佳置换算法&#xff08;OPT&#xff09; 每次选择淘汰的页面将是以后永不使用&#xff0c;或者在最长时间内不再被访问的页面&#xff0c;这样可以保证最低的缺页率 实际上就是从当前内存块…

【Kubernetes】【一】Kubernetes介绍

Kubernetes介绍 应用部署方式演变 在部署应用程序的方式上&#xff0c;主要经历了三个时代&#xff1a; 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点&#xff1a;不能为应用程序定…

PDF SDK for Linux 8.4.2 Crack

PDF SDK for Linux 是适用于任何 Linux 企业或云应用程序的强大解决方案&#xff0c;非常适合需要完全可定制的 PDF 查看器或后端流程的任何 Linux 开发人员。 将 Foxit PDF SDK 嵌入到基于 Linux 的应用程序中非常容易。只需打开您最喜欢的 Linux IDE&#xff0c;复制您需要的…

电容笔和Apple pencil的区别有啥?学生党电容笔推荐

普通的电容笔与 Apple Pencil最大的区别在于&#xff0c;普通的电容笔没有像苹果电容笔那样的重力压感&#xff0c;而仅仅只有一个倾斜的压敢。但平替电容笔的其他性能也很不错&#xff0c;几乎和苹果 Pencil一样&#xff0c;而且平替电容笔的售价只需200元左右。目前&#xff…

react+antd+Table里切换Switch改变状态onChange 传参

场景&#xff1a;table列表里面&#xff0c;操作用Switch切换状态。对应列改变操作在colums里面// 表格行const colums: ColumnsType<potentialType> [{title: useLocale(创建时间),dataIndex: creation_date,key: creation_date,align: center,render: (v: string, rec…

【Python安全编程】Python实现网络主机和端口扫描

文章目录前言环境准备Python实现主机扫描基于ARP协议基于ICMP协议普通版本多线程版本Python实现端口扫描扫描单个端口利用多线程扫描端口后记前言 本文主要讲几个利用Python实现网络扫描的小例子&#xff0c;可以结合多线程或多进程编程改进实例 我曾经走过多遥远的路 跨越过多…

windows环境下安装Nginx及常用操作命令

windows环境下安装Nginx及常用操作命令nginx基本概述基本用途nginx安装nginx基本概述 Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器。基本用途 nginx是一个轻量级高并发服务器&#xff0c;而tomcat并不是。nginx一般被用来做反向代理&#xff0c;将请求转发到应用…

The Social-Engineer Toolkit(社会工程学工具包)互联网第一篇全模块讲解

一、工具介绍 Social-Engineer Toolkit 是一个专为社会工程设计的开源渗透测试框架&#xff0c;可以帮助或辅助你完成二维码攻击、可插拔介质攻击、鱼叉攻击和水坑攻击等。SET 本身提供了大量攻击选项&#xff0c;可让您快速进行信任型攻击&#xff0c;也是一款高度自定义工具…

Fluent Python 笔记 第 14 章 可迭代的对象、迭代器和生成器

迭代是数据处理的基石。扫描内存中放不下的数据集时&#xff0c;我们要找到一种惰性获取数据项的方式&#xff0c;即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern)。本章说明 Python 语言是如何内置迭代器模式的&#xff0c;这样就避免了自己手动去实现。 在 P…