当前位置: 首页 > news >正文

完整教程:Java+Vue+Websocket实现OSS文件上传进度条功能

引言

文件上传是Web应用开发中常见的需求之一,而实时显示文件上传的进度条可以提升用户体验。本教程将介绍如何使用Java后端和Vue前端实现文件上传进度条功能,借助阿里云的OSS服务进行文件上传。

技术栈

  • 后端:Java、Spring Boot 、WebSocket Server
  • 前端:Vue、WebSocket Client

前端实现

安装依赖

npm install websocket sockjs-client

UploadFiles文件上传组件

注意:异步请求接口的时候,后端返回数据结构如下,实际根据自己需求调整返回。

{"code": 200,"message": "成功","data": {"requestId": "file_1697165869563",}
}

创建UploadFiles组件,前端主业务逻辑:上传文件方法和初始化websocket服务方法。这里requestId也是上传文件到OSS的Bucket桶后的文件名,后面Java后端展示逻辑的时候有显示,这里我服务端的端口是8886。实际根据自己需求调整。需要注意的是,后端服务程序启动的时候,端口号是与websocket服务共用的,websocket服务不需要额外设置端口号。

<template><div><input type="file" @change="handleFileChange" /><button @click="uploadFile">上传</button><div>{{ progress }}</div></div>
</template><script>import axios from 'axios';import { Message } from 'element-ui';import { w3cwebsocket as WebSocket } from 'websocket';export default {data() {return {file: null,progress: '0%',requestId: '',websocket: null,isWebSocketInitialized: false,};},methods: {handleFileChange(event) {this.file = event.target.files[0];},initConnect(){if (!this.isWebSocketInitialized) {this.initWebSocket();this.isWebSocketInitialized = true;}},generateUniqueID() {// 使用时间戳来生成唯一IDconst timestamp = new Date().getTime();// 在ID前面添加一个前缀,以防止与其他ID冲突const uniqueID = 'file_' + timestamp;return uniqueID;},uploadFile() {this.initConnect();console.log("isWebSocketInitialized="+this.isWebSocketInitialized)const formData = new FormData();formData.append('file', this.file);formData.append('requestId', this.generateUniqueID());axios.post('http://localhost:8886/test/upload', formData, {headers: {'Content-Type': 'multipart/form-data',},onUploadProgress: (progressEvent) => {this.progress = `${Math.round((progressEvent.loaded * 100) / progressEvent.total)}%`;},}).then((response) => {if(response.data.code===200){this.requestId = response.data.data.requestId;console.log('requestId=' + response.data.data.requestId);}else{// 弹框报错 response.data.messageconsole.log("code="+response.data.code+",message="+response.data.message)Message.error(response.data.message);}this.initWebSocket();}).catch((error) => {console.error('Failed to upload file:', error);});},initWebSocket() {this.websocket = new WebSocket('ws://localhost:8886/test/upload-progress');this.websocket.onmessage = (event) => {const progress = event.data;console.log('上传进度=' + progress);this.progress = progress;// if (progress === '100%') {//   this.websocket.close();// }};this.websocket.onclose = () => {console.log('WebSocket connection closed');};},},};
</script>

使用上传组件

测试演示,所以直接在App.vue中使用UploadFiles组件。

<template><div id="app"><UploadFiles /></div>
</template><script>
import UploadFiles from './components/UploadFiles.vue';export default {name: 'App',components: {UploadFiles}
};
</script>

后端实现

添加依赖

maven中添加socket服务依赖

<!--websocket服务-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

WebSocketConfig配置类

创建WebSocket配置类,配置socket服务注册节点、处理跨域问题和添加监听处理器。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {private final UploadProgressHandler uploadProgressHandler;public WebSocketConfig(UploadProgressHandler uploadProgressHandler) {this.uploadProgressHandler = uploadProgressHandler;}@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(uploadProgressHandler, "/test/upload-progress").setAllowedOrigins("*").addInterceptors(new HttpSessionHandshakeInterceptor());}/*** 引入定时任务bean,防止和项目中quartz定时依赖冲突*/@Bean@Nullablepublic TaskScheduler taskScheduler() {ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler();threadPoolScheduler.setThreadNamePrefix("SockJS-");threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors());threadPoolScheduler.setRemoveOnCancelPolicy(true);return threadPoolScheduler;}}

UploadProgressHandler处理器

创建文件上传进程的处理器,继承TextWebSocketHandler,记录文件上传监听器和记录WebSocketSession会话。

import xxxxxx.PutObjectProgressListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class UploadProgressHandler extends TextWebSocketHandler {private final Map<String, PutObjectProgressListener> uploadProgressMap = new ConcurrentHashMap<>();private static final Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) {uploadProgressMap.forEach((requestId, progressListener) -> {try {session.sendMessage(new TextMessage(progressListener.getProgress()));} catch (IOException e) {e.printStackTrace();}});}@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception{sessionMap.put(session.getId(), session);super.afterConnectionEstablished(session);}public static Map<String, WebSocketSession> getSessionMap() {return sessionMap;}public void addProgressListener(String requestId, PutObjectProgressListener progressListener) {uploadProgressMap.put(requestId, progressListener);}public void removeProgressListener(String requestId) {uploadProgressMap.remove(requestId);}
}

PutObjectProgressListener文件上传监听器

创建文件上传监听器,监听文件上传的进度,并且同步到socket通信会话中

import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressListener;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;import java.io.IOException;/*** 重写上传文件监听器* @author yangz* @date 2023/10/11*/
public class PutObjectProgressListener implements ProgressListener {private final long fileSize;private long bytesWritten = 0;private WebSocketSession session;public PutObjectProgressListener(WebSocketSession session, long fileSize) {this.session = session;this.fileSize = fileSize;}public String getProgress() {if (fileSize > 0) {int percentage = (int) (bytesWritten * 100.0 / fileSize);return percentage + "%";}return "0%";}@Overridepublic void progressChanged(ProgressEvent progressEvent) {bytesWritten += progressEvent.getBytes();if (fileSize > 0) {int percentage = (int) (bytesWritten * 100.0 / fileSize);try {if (session.isOpen()) {session.sendMessage(new TextMessage(percentage + "%"));System.out.println("上传进度="+percentage + "%");}} catch (IOException e) {e.printStackTrace();}}}
}

OSSUtil工具类

创建文件上传工具类

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.socket.WebSocketSession;
import java.io.*;
import java.util.*;
@Slf4j
public class OSSUtil {public static final String endpoint = "http://xxxxx.aliyuncs.com";public static final String accessKeyId = "yourAccessKeyId";public static final String accessKeySecret = "yourAccessKeySecret";private static final String bucketName = yourBucketName;/*** 文件上传并监听进度* @param file,requestId,session* @return {@link String }* @author yangz* @date 2023/10/11*/public static String uploadFile(MultipartFile file, String requestId, WebSocketSession session) throws IOException {OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);// 获取文件大小long fileSize = file.getSize();String originalFilename = file.getOriginalFilename();PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, requestId+originalFilename.substring(originalFilename.lastIndexOf(".")), file.getInputStream());// 文件上传请求附加监听器putObjectRequest.setProgressListener(new PutObjectProgressListener(session,fileSize));ossClient.putObject(putObjectRequest);ossClient.shutdown();return requestId;}
}   

Controller控制器

创建一个测试Controller,API测试文件上传和监听进度

import xxxxxx.UploadProgressHandler;
import xxxxxx.BusinessException;
import xxxxxx.OSSUtil;
import xxxxxx.PutObjectProgressListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.socket.*;
import java.io.IOException;
import java.util.*;@RestController
@Slf4j
@RequestMapping("/test")
public class TestController {@Autowiredprivate UploadProgressHandler uploadProgressHandler;/*** 文件上传并监听进度* @param file* @param requestId* @return {@link Map }<{@link String }, {@link String }>* @author yangz* @date 2023/10/12*/@PostMapping("/upload")public Map<String, String> uploadFile(@RequestParam("file") MultipartFile file, String requestId) throws IOException {//获取处理器监听到的WebSocketSession集合Map<String, WebSocketSession> sessionMap = UploadProgressHandler.getSessionMap();Collection<WebSocketSession> sessions = sessionMap.values();List<WebSocketSession> values = new ArrayList<>(sessions);int size = values.size();if (size<1){throw new BusinessException(500,"Websocket服务未连接!");}// 关闭除最后一个之外的其他WebSocketSessionfor (int i = 0; i < size - 1; i++) {WebSocketSession session = values.get(i);session.close();sessionMap.remove(session.getId());}WebSocketSession webSocketSession = values.get(size-1);//添加websocket服务监听文件上传进程PutObjectProgressListener progressListener = new PutObjectProgressListener(webSocketSession, file.getSize());uploadProgressHandler.addProgressListener(requestId, progressListener);// 将 WebSocketSession 传递给 OSSUtil.uploadFile方法OSSUtil.uploadFile(file, requestId, webSocketSession);//上传完成,移除websocket服务监听uploadProgressHandler.removeProgressListener(requestId);Map<String, String> resultMap = new HashMap<>();resultMap.put("requestId", requestId);return resultMap;}
}

结果展示

步骤:1、选择文件。2、点击上传按钮。3、可以看到进度标签实时展示百分比进度
在这里插入图片描述

结语

通过以上步骤,我们实现了一个包含上传文件和实时显示上传进度的文件上传功能。前端使用Vue编写了上传组件,后端使用Java和Spring Boot进行文件上传处理。通过调用阿里云OSS服务和监听上传文件字节来计算进度,我们能够实时显示文件上传的进度条,提升用户体验。

结束语:人生最大的浪费不是金钱的浪费,而是时间的浪费、认知的迟到

相关文章:

完整教程:Java+Vue+Websocket实现OSS文件上传进度条功能

引言 文件上传是Web应用开发中常见的需求之一&#xff0c;而实时显示文件上传的进度条可以提升用户体验。本教程将介绍如何使用Java后端和Vue前端实现文件上传进度条功能&#xff0c;借助阿里云的OSS服务进行文件上传。 技术栈 后端&#xff1a;Java、Spring Boot 、WebSock…...

【微服务 SpringCloud】实用篇 · 服务拆分和远程调用

微服务&#xff08;2&#xff09; 文章目录 微服务&#xff08;2&#xff09;1. 服务拆分原则2. 服务拆分示例1.2.1 导入demo工程1.2.2 导入Sql语句 3. 实现远程调用案例1.3.1 案例需求&#xff1a;1.3.2 注册RestTemplate1.3.3 实现远程调用1.3.4 查看效果 4. 提供者与消费者 …...

Linux 下I/O操作

一、文件IO 文件 IO 是 Linux 系统提供的接口&#xff0c;针对文件和磁盘进行操作&#xff0c;不带缓存机制&#xff1b;标准IO是C 语言函数库里的标准 I/O 模型&#xff0c;在 stdio.h 中定义&#xff0c;通过缓冲区操作文件&#xff0c;带缓存机制。   标准 IO 和文件 IO 常…...

C#内映射lua表

都是通过同一个方法得到的 例如得到List List<int> list LuaMgr.GetInstance().Global.Get<List<int>>("testList"); 只要把Get的泛型换成对应的类型即可 得到Dictionnary Dictionary<string, int> dic2 LuaMgr.GetInstance().Global…...

android studio检测不到真机

我的情况是&#xff1a; 以前能检测到&#xff0c;有一天我使用无线调试&#xff0c;发现调试有问题&#xff0c;想改为USB调试&#xff0c;但是半天没反应&#xff0c;我就点了手机上的撤销USB调试授权&#xff0c;然后就G了。 解决办法&#xff1a; 我这个情况比较简单&…...

【Eclipse】设置自动提示

前言&#xff1a; eclipse默认有个快捷键&#xff1a;alt /就可以弹出自动提示&#xff0c;但是这样也太麻烦啦&#xff01;每次都需要手动按这个快捷键&#xff0c;下面给大家介绍的是&#xff1a;如何设置敲的过程中就会出现自动提示的教程&#xff01; 先按路线找到需要的页…...

单片机TDL的功能、应用与技术特点 | 百能云芯

在现代电子领域中&#xff0c;单片机&#xff08;Microcontroller&#xff09;是一种至关重要的电子元件&#xff0c;广泛应用于各种应用中。TDL&#xff08;Time Division Multiplexing&#xff0c;时分多路复用&#xff09;是一种数据传输技术&#xff0c;结合单片机的应用&a…...

解决笔记本无线网络5G比2.4还慢的奇怪问题

环境&#xff1a;笔记本Dell XPS15 9570&#xff0c;内置无线网卡Killer Wireless-n/a/ac 1535 Wireless Network Adapter&#xff0c;系统win10家庭版&#xff0c;路由器H3C Magic R2Pro千兆版 因为笔记本用的不多&#xff0c;一直没怎么注意网络速度&#xff0c;直到最近因为…...

GitHub Action 通过SSH 自动部署到云服务器上

准备 正式开始之前&#xff0c;你需要掌握 GitHub Action 的基础语法&#xff1a; workflow &#xff08;工作流程&#xff09;&#xff1a;持续集成一次运行的过程&#xff0c;就是一个 workflow。name: 工作流的名称。on: 指定次工作流的触发器。push 表示只要有人将更改推…...

【AOP系列】7.数据校验

在Java中&#xff0c;我们可以使用Spring AOP&#xff08;面向切面编程&#xff09;和自定义注解来做数据校验。以下是一个简单的示例&#xff1a; 首先&#xff0c;我们创建一个自定义注解&#xff0c;用于标记需要进行数据校验的方法&#xff1a; import java.lang.annotat…...

黑马JVM总结(三十七)

&#xff08;1&#xff09;synchronized-轻量级锁-无竞争 &#xff08;2&#xff09;synchronized-轻量级锁-锁膨胀 重量级锁就是我们前面介绍过的Monitor enter &#xff08;3&#xff09;synchronized-重量级锁-自旋 &#xff08;4&#xff09;synchronized-偏向锁 轻量级锁…...

企业如何通过媒体宣传扩大自身影响力

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 企业可以通过媒体宣传来扩大自身的影响力。可以通过以下的方法。 1. 制定媒体宣传战略&#xff1a; - 首先&#xff0c;制定一份清晰的媒体宣传战略&#xff0c;明确您的宣传目标、目标…...

处理vue直接引入图片地址时显示不出来的问题 src=“[object Module]“

在webpack中使用vue-loader编译template之后&#xff0c;发现图片加载不出来了&#xff0c;开发人员工具中显示src“[object Module]” 这是因为当vue-loader编译template块之后&#xff0c;会将所有的资源url转换为webpack模块请求 这是因为vue使用的是commonjs语法规范&…...

vue3 v-md-editor markdown编辑器(VMdEditor)和预览组件(VMdPreview )的使用

vue3 v-md-editor markdown编辑器和预览组件的使用 概述安装支持vue3版本使用1.使用markdown编辑器 VMdEditor2.markdown文本格式前端渲染 VMdPreview 例子效果代码部分 完整代码 概述 v-md-editor 是基于 Vue 开发的 markdown 编辑器组件 轻量版编辑器 轻量版编辑器左侧编辑…...

java正则表达式 及应用场景爬虫,捕获分组非捕获分组

正则表达式 通常用于校验 比如说qq号 看输入的是否符合规则就可以用这个 public class regex {public static void main(String[] args) {//正则表达式判断qq号是否正确//规则 6位及20位以内 0不能再开头 必须全是数子String qq"1234567890";System.out.println(qq…...

基于 Debian 稳定分支发行版的Zephix 7 发布

Zephix 是一个基于 Debian 稳定版的实时 Linux 操作系统。它可以完全从可移动媒介上运行&#xff0c;而不触及用户系统磁盘上存储的任何文件。 Zephix 是一个基于 Debian 稳定版的实时 Linux 操作系统。它可以完全从可移动媒介上运行&#xff0c;而不触及用户系统磁盘上存储的…...

MBR20100CT-ASEMI肖特基MBR20100CT参数、规格、尺寸

编辑&#xff1a;ll MBR20100CT-ASEMI肖特基MBR20100CT参数、规格、尺寸 型号&#xff1a;MBR20100CT 品牌&#xff1a;ASEMI 芯片个数&#xff1a;2 封装&#xff1a;TO-220 恢复时间&#xff1a;&#xff1e;50ns 工作温度&#xff1a;-65C~175C 浪涌电流&#xff1a…...

修炼k8s+flink+hdfs+dlink(五:安装dockers,cri-docker,harbor仓库)

一&#xff1a;安装docker。&#xff08;所有服务器都要安装&#xff09; 安装必要的一些系统工具 sudo yum install -y yum-utils device-mapper-persistent-data lvm2添加软件源信息 sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/cent…...

github: kex_exchange_identification: Connection closed by remote host

问题描述 (base) ➜ test git:(dev) git pull kex_exchange_identification: Connection closed by remote host Connection closed by 192.30.255.113 port 22 致命错误&#xff1a;无法读取远程仓库。解决方案 参照下边文档 https://docs.github.com/en/authentication/tr…...

AWS香港Web3方案日,防御云安全实践案例受关注

9月26日&#xff0c;AWS合作伙伴之Web3解决方案日在香港举办。来自人工智能、Web3等领域的创业公司、技术专家、风险投资商&#xff0c;就元宇宙时代未来发展进行了深入交流。现场展示了顶象防御云在金融与Web3领域的安全实践案例。 Web3为互联网体系架构的一个整体演进和升级&…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...