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

场景:

当进行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) {

            }
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值