场景:
当进行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值内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论