OSS實現檔案下載進度條顯示
前言
- oss的檔案下載進度條功能官方是提供了各種語言的SDK,但是想要返回資料給前端顯示使用那是不可能的還需要進行修改,樓主專案中後面的模組上傳和下載會用到大檔案上傳下載,所以提前研究一下官方的進度條顯示功能。
- 官方SDK地址: 阿里雲oss下載進度條
實現思路
具體程式碼實現思路的話樓主程式碼註釋十分詳細,可以耐心檢視,畢竟實現功能都是要時間的
- 建立類實現ProgressListener介面: 建立GetObjectProgressListener類實現ProgressListener介面,重寫progressChanged(ProgressEvent progressEvent)方法,該方法會重複回撥顯示下載事件的進度(頻率很高),然後通過成員變數bytesRead記錄下載的位元組數,使用percent記錄當前進度,代表百分之percent,taskId用於記錄當前下載任務的唯一標識,在進入到 TRANSFER_STARTED_EVENT 事件之中時賦值,GetObjectProgressListener用於監聽下載任務的進度,物件中主要包含以下成員變數。
- 建立AsyncMethod類封裝oss下載的函式,用於非同步呼叫方法: 建立AsyncMethod類封裝oss下載的方法(一定要建立類,不然後面非同步呼叫的時候會失效),方法中主要實現oss的帶有進度條下載,傳入GetObjectProgressListener物件用於記錄下載進度
- 建立對應介面用於呼叫oss下載進度條函式: 下載功能都是web請求下載,所以為了測試功能建立對應介面來呼叫測試
- 開通springboot的非同步呼叫機制: 由於oss建立下載任務(如下程式碼)時,會進入阻塞狀態,所以我們需要非同步呼叫AsyncMethod中的progressBarDownload方法。而springboot預設未開啟非同步呼叫,所以需要開啟springboot的非同步呼叫機制
ossClient.getObject(
new GetObjectRequest(bucketName, objectName).
withProgressListener(progressListener),
new File(pathname));
-
配置同步引數類AsyncConfig,其中主要是新增執行緒池操作
-
啟動類中新增@EnableAsync註解,代表開啟非同步呼叫
-
需要非同步呼叫的方法上新增@Async註解
-
瀏覽器中訪問介面,建立下載任務並且報儲存下載進度
-
檢視後臺日誌輸出
相關程式碼實現
- oss依賴和版本
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
- 建立GetObjectProgressListener,實現ProgressListener.裡面需要配置accessKeyId和accessKeySecret。響應位元組轉換事件中的日誌可以開啟,檢視下載過程
package com.luntek.commons.utils.oss;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.event.ProgressListener;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.util.UUID;
/**
* OSS下載進度條功能,用於記錄指定下載任務的下載進度
*
* @author: Czw
* @create: 2020-10-26 13:41
**/
@Slf4j
@Lazy
@Component
@Data
public class GetObjectProgressListener implements ProgressListener {
//已經存好的資料長度
private long bytesRead = 0;
//下載的資料總長度
private long totalBytes = -1;
//下載的當前進度,例如50時,代表下載進度為50%
private int percent = 0;
//是否下載完成
private boolean succeed = false;
//資料在oss中的objectName,也相當於任務ID
private String taskId = "";
// Endpoint以杭州為例,其它Region請按實際情況填寫。
private static final String endpoint = "******";
// RAM賬號進行API訪問或日常運維,請登入RAM控制檯建立RAM賬號。
private static final String accessKeyId = "******";
private static final String accessKeySecret = "******";
//用來儲存所有下載任務進度
public static Map<String,GetObjectProgressListener> progressBarMap=new HashMap<>();
@Override
public void progressChanged(ProgressEvent progressEvent) {
long bytes = progressEvent.getBytes();
ProgressEventType eventType = progressEvent.getEventType();
switch (eventType) {
case TRANSFER_STARTED_EVENT:
//響應開始下載事件
String taskId = UUID.randomUUID().toString();
this.taskId = taskId;
//儲存下載任務
progressBarMap.put(taskId,this);
log.info("***開始下載,progressChanged中的taskId={}***", this.taskId);
break;
case RESPONSE_CONTENT_LENGTH_EVENT:
//響應下載的位元組長度事件
this.totalBytes = bytes;
log.info("***響應下載的位元組長度事件***");
log.info("【{}】 bytes in total will be downloaded to a local file", this.totalBytes);
break;
case RESPONSE_BYTE_TRANSFER_EVENT:
//響應位元組轉換事件
this.bytesRead += bytes;
if (this.totalBytes != -1) {
// log.info("***響應位元組轉換事件***");
int percent = (int) (this.bytesRead * 100.0 / this.totalBytes);
this.percent = percent;
// log.info("***下載進度: {}***", percent + "%(" + this.bytesRead + "/" + this.totalBytes + ")");
} else {
log.info(bytes + " bytes have been read at this time, download ratio: unknown" +
"(" + this.bytesRead + "/...)");
}
break;
case TRANSFER_COMPLETED_EVENT:
//下載完成事件
this.succeed = true;
//移除任務
progressBarMap.remove(this.taskId);
log.info("下載完成, " + this.bytesRead + " bytes have been transferred in total");
break;
case TRANSFER_FAILED_EVENT:
//下載失敗事件
log.info("下載失敗, " + this.bytesRead + " bytes have been transferred");
break;
default:
break;
}
}
}
- 執行緒池非同步配置AsyncConfig(已經有的可以跳過)
package com.luntek.commons.async;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 非同步執行緒池
*
* @author: Czw
* @create: 2020-10-27 18:30
**/
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor myAsync() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//獲取電腦核數
int core = Runtime.getRuntime().availableProcessors();
executor.setCorePoolSize(core);
executor.setMaxPoolSize(core * 2 + 1);
executor.setQueueCapacity(8192);
//執行緒名稱字首
String threadNamePrefix = "MyExecutor-";
executor.setThreadNamePrefix(threadNamePrefix);
// rejection-policy:當pool已經達到max size的時候,如何處理新任務
// CALLER_RUNS:不在新執行緒中執行任務,而是有呼叫者所在的執行緒來執行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
- 封裝非同步方法AsyncMethod(不要懶放在其他類中,一定要加,不聽勸的也可以試試看)
package com.luntek.commons.async;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.GetObjectRequest;
import com.luntek.commons.utils.oss.GetObjectProgressListener;
import com.luntek.commons.utils.oss.OSSUtil;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.io.File;
/**
* 非同步方法
* @author: Czw
* @create: 2020-10-27 20:12
**/
@Component
@Lazy
public class AsyncMethod {
/**
* oss帶有進度條功能下載
*
* @param progressListener 當前下載任務物件
* @param bucketName 儲存名稱
* @param objectName 資料夾路徑+檔名稱,例如abc/efg/123.jpg
* @param pathname oss下載下來的檔案臨時儲存路徑
*/
@Async(value = "myAsync")
public void progressBarDownload(GetObjectProgressListener progressListener,
String bucketName,
String objectName,
String pathname) {
OSS ossClient = new OSSClientBuilder().build("YouEndpoint","YourAccessKeyId","YourAccessKeySecret");
// 下載檔案的同時指定了進度條引數。
ossClient.getObject(
new GetObjectRequest(bucketName, objectName).
withProgressListener(progressListener),
new File(pathname));
// 關閉OSSClient。
ossClient.shutdown();
}
}
- 建立呼叫的api介面和service層
controller
//通過下載任務的taskId獲取下載進度
@GetMapping("/getDownloadProgress")
public ResponseResult<?> getDownloadProgress(String taskId) {
log.info("***getDownloadProgress***");
//返回的資料為空時,代表下載任務未建立或者已經下載完成
//若想區分狀態可以在GetObjectProgressListener類中新增status屬性記錄
return ResponseResult.success(GetObjectProgressListener.progressBarMap.get(taskId));
}
//非同步呼叫oss下載帶有進度條方法,建立下載任務
@Resource
private TestService testService;
@GetMapping("/testOSSSAsynchronous")
public ResponseResult<?> testOSSSAsynchronous() {
log.info("***testOSSSAsynchronous***");
GetObjectProgressListener progressListener = testService.testOSSAsynchronous();
try {
//通過調整休眠時間來檢視下載進度
//第一次生成下載任務返回撥用時也需要休眠200毫秒左右,確保下載進度進入到TRANSFER_STARTED_EVENT事件生成taskId中
Thread.sleep(3000);
log.info("***Thread.sleep***");
} catch (InterruptedException e) {
e.printStackTrace();
}
return ResponseResult.success(progressListener);
}
TestService
GetObjectProgressListener testOSSAsynchronous();
TestServiceImpl
@Override
public GetObjectProgressListener testOSSAsynchronous() {
GetObjectProgressListener objectListener = new GetObjectProgressListener();
asyncMethod.progressBarDownload(
objectListener,
"luntek-resource-cloud-homework-practice",
"test/navicat_39234.zip",
"C:\\Users\\Administrator\\Desktop\\navicat_39234.zip");
log.info("下載完成返回的GetObjectProgressListener ID=【{}】,percent=【{}】",
objectListener.getTaskId(), objectListener.getPercent());
return objectListener;
}
-
啟動類新增註解
@EnableAsync
//掃描類下非同步方法
-
專案包結構
測試效果
測試的資料167M左右,下載速度一般在2-4M/S,我們在程式碼中設定非同步呼叫之後的等待時間,然後返回資料
設定下載的檔案
設定下載檔案
oss客戶端上檢視到的檔案資訊
設定等待時間
前端效果
- 生成taskId
- 檢視任務下載進度
** *基本上下載的程式碼都貢獻出來了,資料整理了4小時,最後希望大家點個贊* **
** *創作不易,轉發註明出處* **
相關問題
- 日誌列印: 引用的sdk中會使用阿帕奇相關方法,所以需要設定org.apache.http.wire的相關日誌級別,具體可以檢視這篇帖子:配置檔案logback-spring.xml
相關文章
- 【CLI】使用 Curl 下載檔案實時進度條顯示
- Python展示檔案下載進度條Python
- 直播系統開發,實現在進度條中顯示文字顯示進度
- vue/js實現檔案流下載,檔案下載進度監聽VueJS
- guzzlehttp/guzzle 配合 symfony/console 命令列中實現檔案下載進度條HTTP命令列
- uni-app中安卓包檢查更新、新版本下載、下載進度條顯示功能實現APP安卓
- LVGL高效顯示進度條
- pyqt5的下載進度條 實現模板QT
- flutter使用dio實現 檔案下載並實現進度監聽總結Flutter
- 實現一個協程帶進度條下載器
- 直播平臺開發,載入網頁、html檔案顯示載入進度網頁HTML
- Simple WPF: S3實現MINIO大檔案上傳並顯示上傳進度S3
- Android多執行緒+單執行緒+斷點續傳+進度條顯示下載Android執行緒斷點
- Android下載檔案(一)下載進度&斷點續傳Android斷點
- Flutter實現檔案下載Flutter
- AJAX實現檔案下載----
- bat檔案進度條程式碼BAT
- 直播平臺製作,Glide載入網路圖,進度條顯示IDE
- Retrofit2.0使用——帶進度下載檔案
- 解決單執行緒QT UI 載入進度條實時顯示方案記錄執行緒QTUI
- a標籤與Blob下載檔案的區別和獲取檔案下載進度
- vue 阿里雲oss下載檔案,自定義命名Vue阿里
- Asp.net C# 檔案下載,附件下載程式碼案例,不顯示檔案路徑ASP.NETC#
- 使用Servlet實現檔案下載Servlet
- Flutter 實現檔案下載功能Flutter
- vc實現https檔案下載HTTP
- Java實現檔案下載功能Java
- 如何實現圖片預載入和載入進度條
- 讀取檔案大小-列印進度條
- HttpWebChilent上傳與下載進度條HTTPWeb
- golang 進度條功能實現Golang
- Android 史上最優雅的實現檔案上傳、下載及進度的監聽Android
- vue專案下載檔案,重新命名,監測進度。Vue
- 記一次 OSS 大批次檔案下載的實現 → bat指令碼不好玩!BAT指令碼
- IDM下載度盤檔案
- Spring Websocket實現文字、圖片、聲音、檔案下載及推送、接收及顯示(叢集模式)SpringWeb模式
- QQ 群檔案下載後過期時間顯示 undefinedUndefined
- vue中頁面載入進度條效果的實現Vue