SpringBoot實(shí)現(xiàn)文件下載的限速功能
前言
在文件下載過程中,如果不加以控制,可能會(huì)導(dǎo)致服務(wù)器帶寬被單個(gè)或少數(shù)用戶占用,影響其他用戶的訪問體驗(yàn)。通過實(shí)現(xiàn)文件下載的限速,可以平衡帶寬資源的使用,確保所有用戶都有良好的下載體驗(yàn)。
實(shí)現(xiàn)思路
為了實(shí)現(xiàn)文件下載的限速,我們需要以下幾個(gè)關(guān)鍵步驟:
- 創(chuàng)建一個(gè)工具類,用于限制下載速率。
- 在控制器中使用該工具類處理文件下載請(qǐng)求。
- 使用
StreamingResponseBody實(shí)現(xiàn)流式響應(yīng),確保大文件可以逐步傳輸。
代碼實(shí)現(xiàn)
步驟1:創(chuàng)建限速工具類
首先,我們創(chuàng)建一個(gè)限速工具類RateLimiter,該類包含一個(gè)方法limitDownloadSpeed,用于限制下載速率。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @desc: 文件工具類
* @author: shy
* @date: 2024/06/28 11:27
*/
public class FileUtil {
private static final int BUFFER_SIZE = 1024;
/**
* 文件下載限速
*
* @param in 輸入流
* @param out 輸出流
* @param bytesPerSecond 每秒允許下載的字節(jié)數(shù)
* @throws IOException
*/
public static void limitDownloadSpeed(InputStream in, OutputStream out, int bytesPerSecond) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
long bytesSent = 0;
long startTime = System.currentTimeMillis();
try {
while ((bytesRead = in.read(buffer)) != -1) {
// 將數(shù)據(jù)寫入輸出流
out.write(buffer, 0, bytesRead);
bytesSent += bytesRead;
if (bytesSent >= bytesPerSecond) {
long elapsedTime = System.currentTimeMillis() - startTime;
if (elapsedTime < 1000) {
// 如果時(shí)間少于1秒,則休眠剩余時(shí)間
Thread.sleep(1000 - elapsedTime);
}
// 重置已發(fā)送字節(jié)計(jì)數(shù)和開始時(shí)間
bytesSent = 0;
startTime = System.currentTimeMillis();
}
}
} catch (InterruptedException e) {
// 恢復(fù)線程的中斷狀態(tài)
Thread.currentThread().interrupt();
throw new IOException("Thread was interrupted", e);
}
}
}
解釋:
BUFFER_SIZE:定義緩沖區(qū)大小。limitDownloadSpeed:通過try-with-resources管理InputStream,根據(jù)設(shè)定的速率讀取數(shù)據(jù)并寫入輸出流,控制傳輸速率。
步驟2:修改文件下載控制器
接下來,我們?cè)诳刂破髦惺褂?code>StreamingResponseBody來實(shí)現(xiàn)文件下載,并調(diào)用限速工具類的方法。
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import com.shy.admin.common.annotation.WithoutLogin;
import com.shy.common.utils.FileUtil;
/**
* @desc: 文件下載Controller
* @author: shy
* @date: 2024/06/28 10:48
*/
@RestController
@RequestMapping("/file")
public class FileController {
// 每秒允許下載的字節(jié)數(shù)(例如100KB/s)
private static final int BYTES_PER_SECOND = 1024 * 100;
@WithoutLogin
@GetMapping("/download/{filename}")
public ResponseEntity<StreamingResponseBody> downloadFile(@PathVariable String filename) {
// 獲取要下載的文件
File file = new File("D:\\tools\\" + filename);
// 使用 StreamingResponseBody 實(shí)現(xiàn)流式響應(yīng)體
StreamingResponseBody responseBody = outputStream -> {
try (InputStream inputStream = Files.newInputStream(file.toPath())) {
// 調(diào)用限速方法
FileUtil.limitDownloadSpeed(inputStream, outputStream, BYTES_PER_SECOND);
}
};
// 返回 ResponseEntity,包含響應(yīng)頭和流式響應(yīng)體
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.contentLength(file.length())
.body(responseBody);
}
}
解釋:
StreamingResponseBody:實(shí)現(xiàn)流式響應(yīng)體,用于處理大文件的逐步傳輸。responseBody:通過lambda表達(dá)式實(shí)現(xiàn)StreamingResponseBody的writeTo方法,在方法中使用try-with-resources管理InputStream,并調(diào)用RateLimiter的方法實(shí)現(xiàn)限速。
工作流程
- 請(qǐng)求處理:當(dāng)客戶端發(fā)送下載請(qǐng)求時(shí),Spring 調(diào)用控制器方法
downloadFile。 - 創(chuàng)建
StreamingResponseBody:控制器方法創(chuàng)建StreamingResponseBody實(shí)例。 - 返回
ResponseEntity:控制器方法返回包含StreamingResponseBody的ResponseEntity,并設(shè)置適當(dāng)?shù)捻憫?yīng)頭(如Content-Disposition和Content-Type)。 - 調(diào)用
writeTo方法:Spring 在準(zhǔn)備向客戶端發(fā)送響應(yīng)時(shí),調(diào)用StreamingResponseBody的writeTo方法,并傳入與客戶端連接的OutputStream。 - 寫入數(shù)據(jù):
writeTo方法中,從文件輸入流讀取數(shù)據(jù),并通過RateLimiter方法將數(shù)據(jù)寫入OutputStream,同時(shí)控制傳輸速率。
總結(jié)
通過以上步驟,我們成功在SpringBoot項(xiàng)目中實(shí)現(xiàn)了文件下載的限速功能。核心思路是通過一個(gè)限速工具類控制數(shù)據(jù)傳輸速率,并使用StreamingResponseBody實(shí)現(xiàn)流式響應(yīng),確保大文件可以逐步傳輸。這種設(shè)計(jì)既能有效控制帶寬資源的使用,又能提供良好的用戶下載體驗(yàn)。
以上就是SpringBoot實(shí)現(xiàn)文件下載的限速功能的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot文件下載限速的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java管道piped輸入流與輸出流應(yīng)用場(chǎng)景案例分析
這篇文章主要介紹了java管道流PipedInputStream與PipedOutputStream(輸入流與輸出流)的應(yīng)用場(chǎng)景案例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02
使用WebUploader實(shí)現(xiàn)上傳文件功能(一)
這篇文章主要為大家詳細(xì)介紹了使用WebUploader實(shí)現(xiàn)上傳文件功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
Java中IP段轉(zhuǎn)CIDR的原理與實(shí)現(xiàn)詳解
CIDR表示的是無類別域間路由,通常形式是IP地址后跟一個(gè)斜杠和數(shù)字,這篇文章主要為大家介紹了如何使用Java實(shí)現(xiàn)IP段轉(zhuǎn)CIDR,需要的可以了解下2025-03-03
springboot框架中如何整合mybatis框架思路詳解
這篇文章主要介紹了springboot框架中如何整合mybatis框架,本文通過示例圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
Java?SpEL表達(dá)式注入入門學(xué)習(xí)示例教程
SpEL是Spring框架特有的表達(dá)式語言,SpEL注入是Java Web題最常見的考點(diǎn)之一,因?yàn)樗壿嬊逦?利用方式很像 PHP 的 eval,本文給大家介紹Java?SpEL表達(dá)式注入入門學(xué)習(xí)示例教程,感興趣的朋友跟隨小編一起看看吧2025-12-12

