完整教程: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应用开发中常见的需求之一,而实时显示文件上传的进度条可以提升用户体验。本教程将介绍如何使用Java后端和Vue前端实现文件上传进度条功能,借助阿里云的OSS服务进行文件上传。 技术栈 后端:Java、Spring Boot 、WebSock…...
【微服务 SpringCloud】实用篇 · 服务拆分和远程调用
微服务(2) 文章目录 微服务(2)1. 服务拆分原则2. 服务拆分示例1.2.1 导入demo工程1.2.2 导入Sql语句 3. 实现远程调用案例1.3.1 案例需求:1.3.2 注册RestTemplate1.3.3 实现远程调用1.3.4 查看效果 4. 提供者与消费者 …...
Linux 下I/O操作
一、文件IO 文件 IO 是 Linux 系统提供的接口,针对文件和磁盘进行操作,不带缓存机制;标准IO是C 语言函数库里的标准 I/O 模型,在 stdio.h 中定义,通过缓冲区操作文件,带缓存机制。 标准 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检测不到真机
我的情况是: 以前能检测到,有一天我使用无线调试,发现调试有问题,想改为USB调试,但是半天没反应,我就点了手机上的撤销USB调试授权,然后就G了。 解决办法: 我这个情况比较简单&…...
【Eclipse】设置自动提示
前言: eclipse默认有个快捷键:alt /就可以弹出自动提示,但是这样也太麻烦啦!每次都需要手动按这个快捷键,下面给大家介绍的是:如何设置敲的过程中就会出现自动提示的教程! 先按路线找到需要的页…...
单片机TDL的功能、应用与技术特点 | 百能云芯
在现代电子领域中,单片机(Microcontroller)是一种至关重要的电子元件,广泛应用于各种应用中。TDL(Time Division Multiplexing,时分多路复用)是一种数据传输技术,结合单片机的应用&a…...
解决笔记本无线网络5G比2.4还慢的奇怪问题
环境:笔记本Dell XPS15 9570,内置无线网卡Killer Wireless-n/a/ac 1535 Wireless Network Adapter,系统win10家庭版,路由器H3C Magic R2Pro千兆版 因为笔记本用的不多,一直没怎么注意网络速度,直到最近因为…...
GitHub Action 通过SSH 自动部署到云服务器上
准备 正式开始之前,你需要掌握 GitHub Action 的基础语法: workflow (工作流程):持续集成一次运行的过程,就是一个 workflow。name: 工作流的名称。on: 指定次工作流的触发器。push 表示只要有人将更改推…...
【AOP系列】7.数据校验
在Java中,我们可以使用Spring AOP(面向切面编程)和自定义注解来做数据校验。以下是一个简单的示例: 首先,我们创建一个自定义注解,用于标记需要进行数据校验的方法: import java.lang.annotat…...
黑马JVM总结(三十七)
(1)synchronized-轻量级锁-无竞争 (2)synchronized-轻量级锁-锁膨胀 重量级锁就是我们前面介绍过的Monitor enter (3)synchronized-重量级锁-自旋 (4)synchronized-偏向锁 轻量级锁…...
企业如何通过媒体宣传扩大自身影响力
传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 企业可以通过媒体宣传来扩大自身的影响力。可以通过以下的方法。 1. 制定媒体宣传战略: - 首先,制定一份清晰的媒体宣传战略,明确您的宣传目标、目标…...
处理vue直接引入图片地址时显示不出来的问题 src=“[object Module]“
在webpack中使用vue-loader编译template之后,发现图片加载不出来了,开发人员工具中显示src“[object Module]” 这是因为当vue-loader编译template块之后,会将所有的资源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 操作系统。它可以完全从可移动媒介上运行,而不触及用户系统磁盘上存储的任何文件。 Zephix 是一个基于 Debian 稳定版的实时 Linux 操作系统。它可以完全从可移动媒介上运行,而不触及用户系统磁盘上存储的…...
MBR20100CT-ASEMI肖特基MBR20100CT参数、规格、尺寸
编辑:ll MBR20100CT-ASEMI肖特基MBR20100CT参数、规格、尺寸 型号:MBR20100CT 品牌:ASEMI 芯片个数:2 封装:TO-220 恢复时间:>50ns 工作温度:-65C~175C 浪涌电流:…...
修炼k8s+flink+hdfs+dlink(五:安装dockers,cri-docker,harbor仓库)
一:安装docker。(所有服务器都要安装) 安装必要的一些系统工具 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 致命错误:无法读取远程仓库。解决方案 参照下边文档 https://docs.github.com/en/authentication/tr…...
AWS香港Web3方案日,防御云安全实践案例受关注
9月26日,AWS合作伙伴之Web3解决方案日在香港举办。来自人工智能、Web3等领域的创业公司、技术专家、风险投资商,就元宇宙时代未来发展进行了深入交流。现场展示了顶象防御云在金融与Web3领域的安全实践案例。 Web3为互联网体系架构的一个整体演进和升级&…...
QT 集成MQTT过程
1 编译库文件 Qt QtMqtt官方源码编译教程_“qtmqtt/qmqttglobal.h”: no such file or directory-CSDN博客 2 参考文献 Qt开发MQTT(一) 之Qt官方Qt MQTT-CSDN博客 QTMQTT 使用MQTT官方库_qt mqtt 官方库-CSDN博客...
GeoServer改造Springboot启动五(解决接口返回xml而不是json)
请求接口返回的是xml,而不是我们常用的json,问题呈现如下图 40 图 40请求接口返回XML 在RequestMapping注解上增加produces {MediaType.APPLICATION_JSON_UTF8_VALUE} 图 41增加produces...
在unity中给游戏物体一个标记
标记 方便识别! 标签(Tag) 引擎内部会对物体的标签建立了索引。通过标签查找物体,要比通过名字查找物体快得多。标签最多只能有 32个。前几个是常用标签,具有特定含义,例如玩家( Player)、主摄摄像机 (Mai…...
【黑马程序员】机器学习
(一)机器学习概述 一、机器学习算法分类 1、监督学习: (1)目标值是类别:分类问题 k-近邻算法、贝叶斯分类、决策树与随机森林、逻辑回归 (2)目标值是连续型的数据:回归…...
flutter card 使用示例
Card组件是卡片组件,内容可以由列表的widget组成,Card组件具有阴影圆角的功能。 常用属性: 属性 说明 margin 外边距elevation 阴影值的深度child 子元素 import package:flutter/material.dart;void main() > runApp(MyApp());class M…...
推荐算法:是否对用户判断能力有影响!!!
首先认识几种常见的推荐算法:推荐算法是一种在信息推送和个性化服务领域常用的技术。它通过分析用户的兴趣、行为和偏好,提供个性化的建议和推荐,以满足用户的需求。以下是对几种常见推荐算法的重新排版,并探讨了它们的作用、影响…...
【OpenVINO】OpenVINO C# API 常用 API 详解与演示
OpenVINO C# API 常用 API 详解与演示 1 安装OpenVINO C# API2 导入程序集 3 初始化OpenVINO 运行时内核4 加载并获取模型信息4.1 加载模型4.2 获取模型信息 5 编译模型并创建推理请求6 张量Tensor6.1 张量的获取与设置6.2 张量的信息获取与设置 7 加载推理数据7.1 获取输入张量…...
django无法导入第三方库
引子 有的人可能会很困惑,为什么自己在pip中安装了某个包,但是在django中死活无法导入。 在cmd中能够导入。 启动django,总是无法导入。 本文将会用一分钟解决你的困惑。 正文 那么本文以上述的第三方库dj_db_conn_pool为例,…...
7-k8s-helm管理
文章目录 一、为什么需要Helm二、Helm相关概念介绍三、Helm安装四、Helm指令介绍五、Helm创建tomcat六、Helm创建tomcat其他方式七、Helm创建redis 一、为什么需要Helm k8s部署:k8s平台部署的服务都是由资源文件描述组成,传统的k8s部署应用需要手工编排…...
零基础怎么样才能学好 Python?Python 入门必看
Python 目前可以用一个字来描述那就是 “火”,问题来了,这么火的语言零基础小白到底该怎样学习 Python? 首先,从基础开始学习,切勿毛躁。 刚开始学习 Python 的时候,我们可能会有些毛躁总觉得这些知识太简…...
哪家网站建设公司靠谱/手机百度正式版
今天在看王爽的《汇编语言》,看到地址总线的时候,由于那个图画的是并行传输,于是我就去搜了下地址总线是不是并行总线,结果看到一篇文章说现在串行总线的传输速度比并行总线要快,我就奇怪了。 在我的印象中,…...
做进行网站推广赚钱/竞价推广什么意思
在用多线程的时候,里面要用到Spring注入服务层,或者是逻辑层的时候,一般是注入不进去的。具体原因应该是线程启动时没有用到Spring实例不池。所以注入的变量值都为null。 如果在run方法里面加载application.xml,来取得bean时&…...
做外贸网站选美国服务器的费用/营销策划推广
德国慕尼黑是Windows到Linux过渡的先锋,它已经投资数百万欧元,放弃依赖微软软件,进而拥抱开源软件,而且它现在准备抛弃最后的数十款Windows应用程序。 在这一点上,慕尼黑市政府部分电脑仍在使用Windows,因为…...
如何用图片做网站背景/百度投流运营
java中int和Integer的区别 一看就懂int 是基本类型,直接存数值integer是对象,用一个引用指向这个对象1.Java 中的数据类型分为基本数据类型和复杂数据类型int 是前者>>integer 是后者(也就是一个类)Integer 是对象类型 int是原始类型 适用场合有很…...
自己怎么做百度网站/深圳网站seo推广
迎接县均衡化国家验收学校解说词办学条件组尊敬的各位专家、各位领导:欢迎莅临我校检查指导工作。我们宁津县第二实验小学始建于1997年,是一所县属非寄宿完全小学。当时只有北面这一座楼,29名教师。2012年秋季扩建,建成南面这座教…...
web开发是做网站/宁德市人社局官网
说明本文假定读者已具备基本的C编译知识。如非特殊说明,文中“源文件”指 * .c文件,“头文件”指 *.h文件,“引用”指包含头文件。一、头文件作用C语言里,每个源文件是一个模块,头文件为使用该模块的用户提供接口。接口…...