Java实现http请求文件流对带宽限速获取md5值

 更新时间:2025年02月28日 09:45:14   作者:曲奇hmb  
文章介绍了如何在进行HTTP请求下载大数据时处理带宽限制和并发问题,通过使用缓冲区和限速逻辑,可以有效控制下载速度,避免掉包和数据丢失,核心公式基于带宽和已下载字节数计算预期耗时,并通过Thread.sleep()进行动态休眠补偿,感兴趣的朋友一起看看吧

场景:

当进行http请求获取大数据的情况,例如下载视频图片的情况,特别是云服务器会有带宽限制,超出带宽会出现掉包丢数据的情况,例如:云服务器限制25m/s的速度,当一个视频下载超过25m/s的速度的时候就会导致掉包,除了限速还需要注意下并发问题,多个线程同时下载也会超出带宽限制,可实现该代码并设置单独的线程池,控制同时并发的线程数来解决带宽问题.

逻辑:

我们可以使用一个缓冲区,每次从输入流读取数据后,先将写入文件的同时更新MD5,这样更高效,因为只需要处理一次数据。这样就不会增加额外的读取次数,避免影响性能。然后,带宽限流的逻辑需要确保在限流的同时处理这两个操作。每次读取数据块后,会计算总字节数和时间,判断是否需要暂停(使用Thread.sleep())。计算MD5的消耗应该相对较小,主要的时间还是在网络读取和文件写入上。

1. 核心公式

限速逻辑基于以下公式实现:

预期耗时(ms)=已下载总字节数×1000最大允许带宽(B/s)预期耗时(ms)=最大允许带宽(B/s)已下载总字节数×1000​

其中 MAX_BPS = NMB/s = N × 1024 × 1024 B/s

2. 控制逻辑分步说明

步骤代码片段说明
1. 记录开始时间long startTime = System.currentTimeMillis();下载任务启动时记录初始时间戳
2. 累计下载量totalBytes += bytesRead;每次读取数据块后更新总字节数
3. 计算理论耗时expectedTime = (totalBytes * 1000) / MAX_BPS根据当前下载量计算理论应耗时
4. 对比实际耗时elapsed = now - startTime获取实际已耗时
5. 动态休眠补偿Thread.sleep(expectedTime - elapsed)强制等待至理论时间线

流程:

1. 打开网络连接,获取输入流。

2. 创建文件输出流和MessageDigest实例。

3. 循环读取数据到缓冲区,每次读取后写入文件,更新MD5(MD5可以边读取文件边计算,防止整个文件读取完再计算导致的内存压力),并进行限流计算。

4. 下载完成后,关闭流,并输出MD5结果。

实现:

 
public static String getMd5(String url, String type) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        final long MAX_BPS = 2 * 1024 * 1024; // 200Mbps = 25MB/s
        MessageDigest md;
        // 创建时间格式化器(线程安全)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5 algorithm not found", e);
        }
        try (CloseableHttpResponse response = httpClient.execute(new HttpGet(url))) {
            HttpEntity entity = response.getEntity();
            if (entity == null) {
                return "";
            }
            // 记录开始时间
            long startTime = System.currentTimeMillis();
            try (InputStream in = entity.getContent()) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                long totalBytes = 0;
                while ((bytesRead = in.read(buffer)) != -1) {
                    md.update(buffer, 0, bytesRead);
                    totalBytes += bytesRead;
                    // 动态限速逻辑
                    long elapsed = System.currentTimeMillis() - startTime;
                    long expectedTime = (totalBytes * 1000) / MAX_BPS;
                    if (elapsed < expectedTime) {
                        Thread.sleep(expectedTime - elapsed);
                    }
                }
                // 计算最终统计信息
                long endTime = System.currentTimeMillis();
                double timeCost = DoubleUtils.formatDouble((endTime - startTime) / 1000.0);
                String sizeMB = String.format("%.3f", DoubleUtils.formatDouble(totalBytes / (1024.0 * 1024)));
                // 生成MD5
                String md5 = DatatypeConverter.printHexBinary(md.digest()).toLowerCase();
                // 完整日志输出
                log.info("下载完成 | 类型: {} | MD5: {} | 大小: {}MB | 耗时: {}s",
                        type, md5, sizeMB, timeCost);
                return md5;
            }
        } catch (Exception e) {
            // 记录异常发生时间
            String errorTime = LocalDateTime.now().format(formatter);
            log.error("下载失败 | 类型: {} | 错误时间: {} | 原因: {}",
                    type, errorTime, e.getMessage());
            return "";
        }finally {
            try {
                httpClient.close();
            }catch (Exception e) {
            }
        }
    }

到此这篇关于Java实现http请求文件流对带宽限速获取md5值的文章就介绍到这了,更多相关java带宽限速获取md5值内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详谈Feign的配置类是如何生效的

    详谈Feign的配置类是如何生效的

    这篇文章主要介绍了Feign的配置类是如何生效的,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • SpringBoot Admin之应用监控与告警配置方式

    SpringBoot Admin之应用监控与告警配置方式

    这篇文章主要介绍了SpringBoot Admin之应用监控与告警配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • Redisson分布式锁实现原理示例详解

    Redisson分布式锁实现原理示例详解

    Redisson分布式锁通过Lua脚本保证原子性操作,实现了分布式互斥、可重入、自动续期等核心特性,其高级特性包括公平锁、读写锁和红锁算法,能够满足不同场景下的并发控制需求,这篇文章主要介绍了Redisson分布式锁实现原理的相关资料,需要的朋友可以参考下
    2026-04-04
  • 关于java.lang.NumberFormatException: null的问题及解决

    关于java.lang.NumberFormatException: null的问题及解决

    这篇文章主要介绍了关于java.lang.NumberFormatException: null的问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Spring MVC如何设置请求头和响应头的Header

    Spring MVC如何设置请求头和响应头的Header

    这篇文章主要介绍了Spring MVC如何设置请求头和响应头的Header问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03
  • java8学习教程之函数引用的使用方法

    java8学习教程之函数引用的使用方法

    这篇文章主要给大家介绍了关于java8学习教程之函数引用的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习下吧。
    2017-09-09
  • 理解HashSet里为什么value不是null

    理解HashSet里为什么value不是null

    你有没有想过Java中HashSet的Value为什么要存PRESENT而不是Null?带着这个疑问来看下边的文章寻找答案吧
    2021-09-09
  • Java notify和notifyAll的区别和相同

    Java notify和notifyAll的区别和相同

    本文主要介绍Java notify和notifyAll的知识,这里整理详细的资料来说明notify 和NotifAll的区别,有需要的小伙伴可以参考下
    2016-09-09
  • mybatis实现批量插入并返回主键(xml和注解两种方法)

    mybatis实现批量插入并返回主键(xml和注解两种方法)

    这篇文章主要介绍了mybatis实现批量插入并返回主键(xml和注解两种方法),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Gson序列化指定忽略字段的三种写法详解

    Gson序列化指定忽略字段的三种写法详解

    在我们日常使用json序列化框架过程中,经常会遇到在输出json字符串时,忽略某些字段,那么在Gson框架中,要想实现这种方式,可以怎么处理呢,本文就来介绍一下
    2021-10-10

最新评论