SpringBoot如何實(shí)現(xiàn)一個(gè)實(shí)時(shí)更新的進(jìn)度條的示例代碼
前言
博主近期接到一個(gè)任務(wù),大概內(nèi)容是:導(dǎo)入excel表格批量修改狀態(tài),期間如果發(fā)生錯(cuò)誤則所有數(shù)據(jù)不成功,為了防止重復(fù)提交,做一個(gè)類似進(jìn)度條的東東。
那么下面我會(huì)結(jié)合實(shí)際業(yè)務(wù)對(duì)這個(gè)功能進(jìn)行分析和記錄。
正文
思路
前端使用bootstrap,后端使用SpringBoot分布式到注冊(cè)中心,原先的想法是導(dǎo)入表格后異步調(diào)用修改數(shù)據(jù)狀態(tài)的方法,然后每次計(jì)算修改的進(jìn)度然后存放在session中,前臺(tái)jquery寫定時(shí)任務(wù)訪問(wèn)獲取session中的進(jìn)度,更新進(jìn)度條進(jìn)度和百分比。但是這存在session在服務(wù)間不共享,跨域問(wèn)題。那么換一個(gè)方式存放,存放在redis中,前臺(tái)定時(shí)任務(wù)直接操作獲取redis的數(shù)據(jù)。
實(shí)施
進(jìn)度條
先來(lái)看一下bootstrap的進(jìn)度條
<div class="progress progress-striped active">
<div class="progress-bar progress-bar-success" role="progressbar"
aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
style="width: 40%;">
40%
</div>
</div>
進(jìn)度條更新主要更新style="width: 40%;"的值即可,div里面的40%可以省略,無(wú)非時(shí)看著明確。
可以考慮將進(jìn)度條放入彈出層。
定時(shí)任務(wù)
//點(diǎn)擊確認(rèn)導(dǎo)入執(zhí)行此方法
function bulkImportChanges() {
//獲取批量操作狀態(tài)文件
var files = $("#importChanges").prop("files");
var changesFile = files[0];
var formData = new FormData();
formData.append("importFile",changesFile);
$.ajax({
type : 'post',
url : "/risk/bulk***es",
data : formData,
processData : false, //文件ajax上傳要加這兩個(gè)的,要不然上傳不了
contentType : false, //
success : function(obj) {
//導(dǎo)入成功
if (obj.rspCode == "00") {
//定時(shí)任務(wù)獲取redis導(dǎo)入修改進(jìn)度
var progress = "";
var timingTask = setInterval(function(){
$.ajax({
type: 'post',
url: "/risk/t***k",
dataType : 'json',
success: function(result) {
progress = result.value;
if (progress != "error"){
var date = progress.substring(0,6);
//這里更新進(jìn)度條的進(jìn)度和數(shù)據(jù)
$(".progress-bar").width(parseFloat(date)+"%");
$(".progress-bar").text(parseFloat(date)+"%");
}
}
});
//導(dǎo)入修改完成或異常(停止定時(shí)任務(wù))
if (parseInt(progress)==100 || progress == "error") {
//清除定時(shí)執(zhí)行
clearInterval(timingTask);
$.ajax({
type: 'post',
url: "/risk/de***ess",
dataType : 'json',
success: function(result) {
$("#bulkImportChangesProcessor").hide();
if (parseInt(progress) == 100) {
alert("批量導(dǎo)入修改狀態(tài)成功");
}
if (progress == "error") {
alert("批量導(dǎo)入修改狀態(tài)失敗");
}
//獲取最新數(shù)據(jù)
window.location.href="/risk/re***ByParam" rel="external nofollow" rel="external nofollow" ;
}
});
}
}, 1000)
}else {
$("#bulkImportChangesProcessor").hide();
alert(obj.rspMsg);
window.location.href="/risk/re***ByParam" rel="external nofollow" rel="external nofollow" ;
}
}
});
}
解釋:點(diǎn)擊確認(rèn)導(dǎo)入文件后成功后開(kāi)啟定時(shí)任務(wù)每一秒(一千毫秒)訪問(wèn)一次后臺(tái)獲取redis存放的進(jìn)度,返回更新進(jìn)度條,如果更新完成或者更新失?。ǜ鶕?jù)后臺(tái)返回的數(shù)據(jù)決定)則停止定時(shí)任務(wù)顯示相應(yīng)的信息并刷新頁(yè)面。獲取最新數(shù)據(jù)。
后臺(tái)控制層
/**
* 退單管理批量修改狀態(tài)導(dǎo)入文件
* @param importFile
* @return
*/
@ResponseBody
@RequestMapping("/bulk***es")
public Map<String,Object> bulk***es(MultipartFile importFile){
log.info("退單管理批量修改狀態(tài)導(dǎo)入文件,傳入?yún)?shù):"+importFile);
Map<String,Object> map = new HashMap<>();
List<Bulk***esEntity> fromExcel = null;
try{
//使用工具類導(dǎo)入轉(zhuǎn)成list
String[] header = {"sy***um","t***mt","ha***ult","re***nd","sy***nd","r**k"};
fromExcel = importExcelUtil.importDataFromExcel(importFile, header, BulkImportChangesEntity.class);
if (fromExcel.size()==0){
map.put("rspCode","99");
map.put("rspMsg","導(dǎo)入數(shù)據(jù)不能為空");
return map;
}
}catch (Exception e){
map.put("rspCode","99");
map.put("rspMsg","導(dǎo)入操作表失敗,請(qǐng)注意數(shù)據(jù)列格式");
return map;
}
try {
//這里會(huì)對(duì)list集合中的數(shù)據(jù)進(jìn)行處理
log.info("調(diào)用服務(wù)開(kāi)始,參數(shù):"+JSON.toJSONString(fromExcel));
//String url = p4_zuul_url+"/***/ri***eat/bu***nges";
String url = p4_zuul_url+"/***-surpass/ri***eat/bu***nges";
String result = HttpClientUtil.doPost(url,JSON.toJSONString(fromExcel));
log.info("調(diào)用服務(wù)結(jié)束,返回?cái)?shù)據(jù):"+result);
if (result != null){
map = JSONObject.parseObject(result, Map.class);
log.info("批量修改狀態(tài)導(dǎo)入:"+JSON.toJSONString(map));
}
}catch (Exception e){
map.put("rspCode","99");
map.put("rspMsg","導(dǎo)入操作表失敗");
log.info("bu***es exception",e);
return map;
}
return map;
}
/**
* 獲取退單管理批量修改狀態(tài)導(dǎo)入文件進(jìn)度條進(jìn)度
* @return
*/
@ResponseBody
@RequestMapping("/t***sk")
public Map<String,Object> t***sk(){
Map<String,Object> map = new HashMap<>();
//獲取redis值
String progress = HttpClientUtil.doGet(
p4_zuul_url + "/" + p4_redis + "/redis***ler/get?key=progressSchedule");
if (progress != null){
map = JSONObject.parseObject(progress, Map.class);
log.info("進(jìn)度條進(jìn)度:"+JSON.toJSONString(map));
map.put("progressSchedule",progress);
}else {
HttpClientUtil.doGet(
p4_zuul_url + "/" + p4_redis + "/redis***ler/del?key=progressSchedule");
}
return map;
}
/**
* 清除redis進(jìn)度條進(jìn)度
* @return
*/
@ResponseBody
@RequestMapping("/de***ess")
public Map<String,Object> de***ess(){
Map<String,Object> map = new HashMap<>();
String progress = HttpClientUtil.doGet(
p4_zuul_url + "/" + p4_redis + "/redis***ler/del?key=progressSchedule");
if (progress != null){
map = JSONObject.parseObject(progress, Map.class);
log.info("返回?cái)?shù)據(jù):"+JSON.toJSONString(map));
}
return map;
}
導(dǎo)入時(shí)調(diào)用第一個(gè)bulk***es方法,定時(shí)任務(wù)調(diào)用t***sk方法,導(dǎo)入完成或發(fā)生錯(cuò)誤調(diào)用de***ess方法刪除redis數(shù)據(jù),避免占用資源。
服務(wù)層
@Async//開(kāi)啟異步
@Transactional(rollbackFor = Exception.class)//事務(wù)回滾級(jí)別
@Override
public void bulkImportChanges(List<BulkImportChangesParam> list) {
//初始化進(jìn)度
Double progressBarSchedule = 0.0;
redisClient.set("progressSchedule", progressBarSchedule + "");//存redis
try {
for (int i = 1; i <= list.size(); i++) {
RiskRetreatEntity entity = riskRetreatMapper.selectRetreatListBySysRefNum(list.get(i-1).getSysRefNum());
if (entity == null){
//查詢結(jié)果為空直接進(jìn)行下次循環(huán)不拋出
continue;
}
//實(shí)體封裝
···
//更新
riskRetreatMapper.updateRetreatByImport(entity);
//計(jì)算修改進(jìn)度并存放redis保存(1.0 / list.size())為一條數(shù)據(jù)進(jìn)度
progressBarSchedule = (1.0 / list.size()) * i*100;
redisClient.set("progressSchedule", progressBarSchedule+"");
if (i==list.size()){
redisClient.set("progressSchedule", "100");
}
}
}catch (Exception e){
//當(dāng)發(fā)生錯(cuò)誤則清除緩存直接拋出回滾
redisClient.set("progressSchedule","error");
log.info("導(dǎo)入更新錯(cuò)誤,回滾");
log.info("bulkImportChanges exception:",e);
throw e;
}
}
每更新一條數(shù)據(jù)存放進(jìn)度,當(dāng)發(fā)生錯(cuò)誤則進(jìn)行回滾。如果開(kāi)啟異步則需要在啟動(dòng)類添加注解@EnableAsync。
@EnableAsync
···//其他注解
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
結(jié)果樣式

結(jié)尾
這次結(jié)合了前端的定時(shí)任務(wù),后臺(tái)事務(wù)以及異步,總的來(lái)說(shuō)還是一次🙅不錯(cuò)的體驗(yàn)。
到此這篇關(guān)于SpringBoot如何實(shí)現(xiàn)一個(gè)實(shí)時(shí)更新的進(jìn)度條的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot實(shí)時(shí)更新進(jìn)度條內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 如何通過(guò)Magic 魔數(shù)獲取文件類型
魔數(shù)有很多種定義,這里我們討論的主要是在編程領(lǐng)域的定義,文件的起始幾個(gè)字節(jié)的內(nèi)容是固定的,本文給大家介紹Java Magic 魔數(shù)獲取文件類型的相關(guān)知識(shí),感興趣的朋友一起看看吧2023-11-11
靈活控制任務(wù)執(zhí)行時(shí)間的Cron表達(dá)式范例
這篇文章主要為大家介紹了靈活控制任務(wù)執(zhí)行時(shí)間的Cron表達(dá)式范例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
springboot實(shí)現(xiàn)對(duì)注解的切面案例
這篇文章主要介紹了springboot實(shí)現(xiàn)對(duì)注解的切面過(guò)程,首先定義一個(gè)注解、再編寫對(duì)注解的切面只是記錄的執(zhí)行時(shí)間和打印方法,可以實(shí)現(xiàn)其他邏輯,需要的朋友可以參考一下2022-01-01
一文帶你掌握SpringBoot中常見(jiàn)定時(shí)任務(wù)的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Spring?Boot中定時(shí)任務(wù)的基本用法、高級(jí)特性以及最佳實(shí)踐,幫助開(kāi)發(fā)人員更好地理解和應(yīng)用定時(shí)任務(wù),提高系統(tǒng)的穩(wěn)定性和可靠性,需要的可以參考下2024-03-03
JavaWeb監(jiān)聽(tīng)器Listener實(shí)例解析
這篇文章主要為大家詳細(xì)介紹了JavaWeb監(jiān)聽(tīng)器Listener實(shí)例,針對(duì)監(jiān)聽(tīng)器進(jìn)行進(jìn)行細(xì)致分析,感興趣的小伙伴們可以參考一下2016-08-08
maven中springboot-maven-plugin的5種打包方式
本文主要介紹了maven中springboot-maven-plugin的5種打包方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-09-09
解決使用@RequestParam注解和泛型遇到的問(wèn)題
這篇文章主要介紹了解決使用@RequestParam注解和泛型遇到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
java javax.annotation.Resource注解的詳解
這篇文章主要介紹了javax.annotation.Resource注解的詳解的相關(guān)資料,需要的朋友可以參考下2016-10-10

