Java實(shí)現(xiàn)http請(qǐng)求文件流對(duì)帶寬限速獲取md5值
場(chǎng)景:
當(dāng)進(jìn)行http請(qǐng)求獲取大數(shù)據(jù)的情況,例如下載視頻圖片的情況,特別是云服務(wù)器會(huì)有帶寬限制,超出帶寬會(huì)出現(xiàn)掉包丟數(shù)據(jù)的情況,例如:云服務(wù)器限制25m/s的速度,當(dāng)一個(gè)視頻下載超過25m/s的速度的時(shí)候就會(huì)導(dǎo)致掉包,除了限速還需要注意下并發(fā)問題,多個(gè)線程同時(shí)下載也會(huì)超出帶寬限制,可實(shí)現(xiàn)該代碼并設(shè)置單獨(dú)的線程池,控制同時(shí)并發(fā)的線程數(shù)來解決帶寬問題.
邏輯:
我們可以使用一個(gè)緩沖區(qū),每次從輸入流讀取數(shù)據(jù)后,先將寫入文件的同時(shí)更新MD5,這樣更高效,因?yàn)橹恍枰幚硪淮螖?shù)據(jù)。這樣就不會(huì)增加額外的讀取次數(shù),避免影響性能。然后,帶寬限流的邏輯需要確保在限流的同時(shí)處理這兩個(gè)操作。每次讀取數(shù)據(jù)塊后,會(huì)計(jì)算總字節(jié)數(shù)和時(shí)間,判斷是否需要暫停(使用Thread.sleep())。計(jì)算MD5的消耗應(yīng)該相對(duì)較小,主要的時(shí)間還是在網(wǎng)絡(luò)讀取和文件寫入上。
1. 核心公式
限速邏輯基于以下公式實(shí)現(xiàn):
預(yù)期耗時(shí)(ms)=已下載總字節(jié)數(shù)×1000最大允許帶寬(B/s)預(yù)期耗時(shí)(ms)=最大允許帶寬(B/s)已下載總字節(jié)數(shù)×1000?

其中 MAX_BPS = NMB/s = N × 1024 × 1024 B/s
2. 控制邏輯分步說明
| 步驟 | 代碼片段 | 說明 |
|---|---|---|
| 1. 記錄開始時(shí)間 | long startTime = System.currentTimeMillis(); | 下載任務(wù)啟動(dòng)時(shí)記錄初始時(shí)間戳 |
| 2. 累計(jì)下載量 | totalBytes += bytesRead; | 每次讀取數(shù)據(jù)塊后更新總字節(jié)數(shù) |
| 3. 計(jì)算理論耗時(shí) | expectedTime = (totalBytes * 1000) / MAX_BPS | 根據(jù)當(dāng)前下載量計(jì)算理論應(yīng)耗時(shí) |
| 4. 對(duì)比實(shí)際耗時(shí) | elapsed = now - startTime | 獲取實(shí)際已耗時(shí) |
| 5. 動(dòng)態(tài)休眠補(bǔ)償 | Thread.sleep(expectedTime - elapsed) | 強(qiáng)制等待至理論時(shí)間線 |
流程:
1. 打開網(wǎng)絡(luò)連接,獲取輸入流。
2. 創(chuàng)建文件輸出流和MessageDigest實(shí)例。
3. 循環(huán)讀取數(shù)據(jù)到緩沖區(qū),每次讀取后寫入文件,更新MD5(MD5可以邊讀取文件邊計(jì)算,防止整個(gè)文件讀取完再計(jì)算導(dǎo)致的內(nèi)存壓力),并進(jìn)行限流計(jì)算。
4. 下載完成后,關(guān)閉流,并輸出MD5結(jié)果。
實(shí)現(xiàn):
public static String getMd5(String url, String type) {
CloseableHttpClient httpClient = HttpClients.createDefault();
final long MAX_BPS = 2 * 1024 * 1024; // 200Mbps = 25MB/s
MessageDigest md;
// 創(chuàng)建時(shí)間格式化器(線程安全)
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 "";
}
// 記錄開始時(shí)間
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;
// 動(dòng)態(tài)限速邏輯
long elapsed = System.currentTimeMillis() - startTime;
long expectedTime = (totalBytes * 1000) / MAX_BPS;
if (elapsed < expectedTime) {
Thread.sleep(expectedTime - elapsed);
}
}
// 計(jì)算最終統(tǒng)計(jì)信息
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 | 耗時(shí): {}s",
type, md5, sizeMB, timeCost);
return md5;
}
} catch (Exception e) {
// 記錄異常發(fā)生時(shí)間
String errorTime = LocalDateTime.now().format(formatter);
log.error("下載失敗 | 類型: {} | 錯(cuò)誤時(shí)間: {} | 原因: {}",
type, errorTime, e.getMessage());
return "";
}finally {
try {
httpClient.close();
}catch (Exception e) {
}
}
}到此這篇關(guān)于Java實(shí)現(xiàn)http請(qǐng)求文件流對(duì)帶寬限速獲取md5值的文章就介紹到這了,更多相關(guān)java帶寬限速獲取md5值內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis?Generator?ORM層面的代碼自動(dòng)生成器(推薦)
Mybatis?Generator是一個(gè)專門為?MyBatis和?ibatis框架使用者提供的代碼生成器,也可以快速的根據(jù)數(shù)據(jù)表生成對(duì)應(yīng)的pojo類、Mapper接口、Mapper文件,甚至生成QBC風(fēng)格的查詢對(duì)象,這篇文章主要介紹了MyBatis?Generator?ORM層面的代碼自動(dòng)生成器,需要的朋友可以參考下2023-01-01
10k+點(diǎn)贊的 SpringBoot 后臺(tái)管理系統(tǒng)教程詳解
這篇文章主要介紹了10k+點(diǎn)贊的 SpringBoot 后臺(tái)管理系統(tǒng)教程詳解,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
SpringCloud-Gateway轉(zhuǎn)發(fā)WebSocket失敗問題及解決
這篇文章主要介紹了SpringCloud-Gateway轉(zhuǎn)發(fā)WebSocket失敗問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
適用于Java初學(xué)者的學(xué)習(xí)路線圖
這篇文章主要介紹了學(xué)習(xí)Java的路線圖的五個(gè)必經(jīng)階段,還有一些作者的想法分享給大家,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
兼容Spring Boot 1.x和2.x配置類參數(shù)綁定的工具類SpringBootBindUtil
今天小編就為大家分享一篇關(guān)于兼容Spring Boot 1.x和2.x配置類參數(shù)綁定的工具類SpringBootBindUtil,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12
4個(gè)Java8中你需要知道的函數(shù)式接口分享
Java?8?中提供了許多函數(shù)式接口,包括Function、Consumer、Supplier、Predicate?等等。本文主要來和大家介紹一下它們的具體使用,需要的可以參考一下2023-04-04
基于java springboot + mybatis實(shí)現(xiàn)電影售票管理系統(tǒng)
這篇文章主要介紹了基于java springboot + mybatis實(shí)現(xiàn)的完整電影售票管理系統(tǒng)基于java springboot + mybatis,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
SpringBoot整合Security實(shí)現(xiàn)權(quán)限控制框架(案例詳解)
Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問控制解決方案的安全框,是一個(gè)重量級(jí)的安全管理框架,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-08-08

