首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >异步上传石墨文件进度条前端展示记录(采用Redis中String数据结构实现)

异步上传石墨文件进度条前端展示记录(采用Redis中String数据结构实现)

原创
作者头像
舒一笑不秃头
发布2025-05-28 22:54:51
发布2025-05-28 22:54:51
1630
举报
文章被收录于专栏:技术方案设计技术方案设计

> 事件起因是客户现场需要从石墨文档中获取文件信息,文件信息存在存在多个,进行批量上传。为了用户的友好型体验,需要做进行条展示的方式,具体实现见下文.....

## 上传流程介绍

石墨文档支持从链接🔗方式获取文件信息,通过对文件链接的截取,会得到16位编码的值。同时不仅仅针对单文档,也支持指定文件夹或者空间下的方式,只需要对对应的文档或者文件夹📁或者空间添加对应的协作者(石墨文档API下载需要私有化部署之后才可以提供,官方不带开放平台)。就可以通过对应的下载文档接口进行对应的逻辑下载,关于如何下载的大致流程如下图所示。本次只介绍进度条逻辑如何实现,关于如何上传本次文章不做展开。

![](https://shuyixiao.oss-cn-hangzhou.aliyuncs.com/image-20250510171748114.png)

## 服务流程示意图

用户层面结合技术层面大致的交互流程是,用户首先从石墨文档获取需要上传的文档链接,进行录入到对应的系统界面。调用后端服务的方式,由后端服务通过配置相关密钥与协作者(石墨管理员)获取相关文档信息。将获取到的相关文档之后,在业务服务中进行与redis交互,存储进入进度。

![](https://shuyixiao.oss-cn-hangzhou.aliyuncs.com/image-20250510173646359.png)

## Talk is easy show me the code

### redis存储类实体类

```java

public class RedisTaskProcess {

@Schema(description = "消息id")

private String taskId;

@Schema(description = "业务类型,定义自己的任务类型xxxxx")

private String businessType;

@Schema(description = "处理进度的百分比")

private BigDecimal processPercent;

@Schema(description = "任务状态, processing|completed|error|cancel")

private String status = "processing";

@Schema(description = "消息描述, 异常时或者特殊场景会指定消息")

private String msg;

@Schema(description = "用户编码,用于获取任务Key")

private String userCode;

@Schema(description = "任务标题(文件名)")

private String title;

@Schema(description = "创建时间")

private Long createTime;

/**

* 启动一个长任务处理流程,并返回任务处理响应对象。

*

* @param businessType 业务类型,用于标识任务的业务场景。

* @param userCode 用户代码,标识发起任务的用户。

* @param title 任务标题,用于描述任务内容。

* @param redisTemplate Redis模板对象,用于将任务信息存储到Redis中。

* @return LongTaskProcessResponse 返回长任务处理响应对象,包含任务ID、状态、进度等信息。

*/

public static RedisTaskProcess commonStart(String businessType, String userCode, String title, RedisTemplate<String, Object> redisTemplate) {

// 初始化长任务处理响应对象

RedisTaskProcess res = new RedisTaskProcess();

// 设置任务ID、业务类型、状态、消息、标题、进度、用户代码和创建时间

res.setTaskId(IdUtil.getSnowflakeNextIdStr());

res.setBusinessType(businessType);

res.setStatus("processing");

res.setMsg("开始处理");

res.setTitle(title);

res.setProcessPercent(new BigDecimal(1));

res.setUserCode(userCode);

res.setCreateTime(System.currentTimeMillis());

// 将任务信息存储到Redis中,并设置过期时间为1天

redisTemplate.opsForValue().set(res.findTaskCacheKey(), res, 1, TimeUnit.DAYS);

return res;

}

/**

* 完成任务的通用方法,用于设置任务状态为“已完成”,并更新相关信息到Redis缓存中。

*

* @param completeMsg 任务完成时的消息内容,用于设置任务的msg字段。

* @param redisTemplate Redis操作模板,用于将任务信息存储到Redis缓存中。

*/

public void commonComplete(String completeMsg, RedisTemplate<String, Object> redisTemplate) {

// 设置任务状态为“已完成”

this.setStatus("completed");

// 设置任务完成消息

this.setMsg(completeMsg);

// 设置任务进度为100%

this.setProcessPercent(new BigDecimal(100));

// 设置任务创建时间为当前时间

this.setCreateTime(System.currentTimeMillis());

// 将任务信息存储到Redis缓存中,并设置过期时间为1天

redisTemplate.opsForValue().set(this.findTaskCacheKey(), this, 1, TimeUnit.DAYS);

}

/**

* 执行通用的更新操作,并调用重载的 `commonUpdate` 方法。

*

* @param status 当前状态信息,通常用于表示操作的状态。

* @param msg 更新操作的消息或描述信息。

* @param addPercent 需要增加的百分比值,通常用于表示进度或比例的增加。

* @param redisTemplate Redis 操作模板,用于与 Redis 进行交互。

*/

public void commonUpdate(String status, String msg, Integer addPercent, RedisTemplate<String, Object> redisTemplate) {

this.commonUpdate(status, msg, addPercent, null, redisTemplate);

}

/**

* 更新任务状态信息,并将更新后的任务信息存储到Redis中。

*

* @param status 任务状态,用于设置当前任务的状态。

* @param msg 任务消息,用于设置当前任务的消息内容。

* @param addPercent 增加的进度百分比,用于计算当前任务的进度。

* @param title 任务标题,如果非空则更新任务的标题。

* @param redisTemplate Redis模板对象,用于将任务信息存储到Redis中。

*/

public void commonUpdate(String status, String msg, Integer addPercent, String title, RedisTemplate<String, Object> redisTemplate) {

// 设置任务状态和消息

this.setStatus(status);

this.setMsg(msg);

// 如果提供了任务标题,则更新标题

if (title != null) {

this.setTitle(title);

}

// 计算并更新任务进度,确保进度不超过99%

BigDecimal calRes = this.getProcessPercent().add(new BigDecimal(addPercent));

this.setProcessPercent(calRes.min(new BigDecimal(99)));

// 设置任务的创建时间

this.setCreateTime(System.currentTimeMillis());

// 将更新后的任务信息存储到Redis中,并设置过期时间为1天

redisTemplate.opsForValue().set(this.findTaskCacheKey(), this, 1, TimeUnit.DAYS);

}

/**

* 处理通用失败情况,并将失败信息存储到Redis中。

*

* 该方法将当前对象的状态设置为"error",并设置失败信息。同时,记录当前时间作为创建时间,

* 并将整个对象存储到Redis中,缓存时间为1天。

*

* @param failMsg 失败信息,用于描述失败的原因或详情。

* @param redisTemplate Redis操作模板,用于与Redis进行交互。

*/

public void commonFailure(String failMsg, RedisTemplate<String, Object> redisTemplate) {

// 设置状态为"error",表示任务失败

this.setStatus("error");

// 设置失败信息

this.setMsg(failMsg);

// 记录当前时间作为创建时间

this.setCreateTime(System.currentTimeMillis());

// 将当前对象存储到Redis中,缓存时间为1天

redisTemplate.opsForValue().set(this.findTaskCacheKey(), this, 1, TimeUnit.DAYS);

}

/**

* 生成任务缓存的唯一键。

*

* 该函数通过将任务处理前缀、用户代码和任务ID拼接成一个字符串,生成一个唯一的缓存键。

* 该键通常用于在缓存系统中标识和存储与特定任务相关的数据。

*

* @return 返回一个格式化的字符串,表示任务缓存的唯一键。格式为:"任务处理前缀:用户代码:任务ID"。

*/

public String findTaskCacheKey() {

return String.format("%s:%s:%s", TASK_PROCESS_PREFIX_KEY, this.userCode, this.taskId);

}

}

```

### 上传服务伪代码

```java

public void updatexxxx{

try{

// 业务测文件集合

List fileIdList = new ArrayList();

// 业务代码填充

xxxxxxx

// 初始化进度响应对象

RedisTaskProcess redisTaskProcess = RedisTaskProcess.commonStart("业务代码","业务代码","业务代码");

// 计算文件进度

int documentationProgress = 99 / fileIdList.size();

// 指定上传逻辑伪代码

fileIdList.forEach(fileId -> {

// 业务代码逻辑

xxxxxx

// 更新文件进度

redisTaskProcess..commonUpdate("processing", "正在处理文件: " + save.getFileName(), progressIncrement, redisTemplate);

})

// 完成长传逻辑

commonComplete("文档下载完成", redisTemplate);

} catch {

log.error("文档下载处理失败", e);

redisTaskProcess.commonFailure("文档下载处理失败: " + e.getMessage(), redisTemplate);

throw new RuntimeException(e);

}

}

```

> 上述方式采用redis的string数据结构进行存储进度,存在些许弊端。在高并发场景下需要额外考虑乐观锁等机制避免数据覆盖,需要使用`WATCH`命令或Lua脚本确保原子性。上述问题将在下一篇文章中进行解决...

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档