完整教程: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服务和监听上传文件字节来计算进度,我们能够实时显示文件上传的进度条,提升用户体验。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/104262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java学习之道】GUI开发的基本概念

引言 在这一章&#xff0c;我们将一起走进Java的图形用户界面&#xff08;GUI&#xff09;开发的世界。在你阅读完这篇文章后&#xff0c;你将能够了解什么是GUI&#xff0c;以及如何使用Java进行GUI的开发。 一、什么是GUI 首先&#xff0c;让我们来解答一个许多初学者都会…

AN基础工具——变形工具

【AN基础工具——变形工具】 基本使用方法&#xff1a;任意变形工具基础动画 本篇内容&#xff1a;学会使用变形工具 重点内容&#xff1a;变形工具 工 具&#xff1a;Adobe Animate 2022 基本使用方法&#xff1a; 任意变形工具 《任意变形工具&#xff08;快捷键Q&#xff0…

深度学习系列51:hugging face加速库optimum

1. 普通模型 Optimum是huggingface transformers库的一个扩展包&#xff0c;用来提升模型在指定硬件上的训练和推理性能。Optimum支持多种硬件&#xff0c;不同硬件下的安卓方式如下&#xff1a; 如果是国内安装的话&#xff0c;记得加上-i https://pypi.tuna.tsinghua.edu.c…

八大排序算法(含时间复杂度、空间复杂度、算法稳定性)

文章目录 八大排序算法(含时间复杂度、空间复杂度、算法稳定性)1、&#xff08;直接&#xff09;插入排序1.1、算法思想1.2、排序过程图解1.3、排序代码 2、希尔排序3、冒泡排序3.1、算法思想3.2、排序过程图解3.3、排序代码 4、&#xff08;简单&#xff09;选择排序4.1、算法…

Springcloud笔记(2)-Eureka服务注册中心

Eureka服务注册 Eureka作为一个微服务的治理中心&#xff0c;它是一个服务应用&#xff0c;可以接收其他服务的注册&#xff0c;也可以发现和治理服务实例。 服务治理中心是微服务&#xff08;分布式&#xff09;架构中最基础和最核心的功能组件&#xff0c;它主要对各个服务…

kafka生产者发送消息报错 Bootstrap broker localhost:9092 (id: -1 rack: null) disconnected

报这个错误是因为kafka里的配置要修改下 在config目录下 server.properties配置文件 这下发送消息就不会一直等待&#xff0c;就可以发送成功了

CTFHub SSRF 题目

文章目录 CTFHub SSRF 通关教程1. 内网访问&#xff0c;伪协议利用1.1 内网访问1.2 伪协议读取文件1.3 端口扫描 2. POST请求&#xff0c;上传文件&#xff0c;FastCGI协议&#xff0c;Redis协议2.1 POST请求2.2 上传文件2.3 FastCGI协议2.4 Redis协议 3. Bypass系列3.1 URL By…

Macos数字音乐库:Elsten Software Bliss for Mac

Elsten Software Bliss for Mac是一款优秀的音乐管理软件&#xff0c;它可以帮助用户自动化整理和标记数字音乐库&#xff0c;同时可以自动识别音乐信息并添加标签和元数据。 此外&#xff0c;Bliss还可以修复音乐库中的问题&#xff0c;例如重复的音乐文件和缺失的专辑封面等…

Apache Dubbo 首个 Node.js 3.0-alpha 版本正式发布

作者&#xff1a;蔡建怿 关于Apache Dubbo3 Apache Dubbo 是一款易用、高性能的 WEB 和 RPC 框架&#xff0c;同时为构建企业级微服务提供服务发现、流量治理、可观测、认证鉴权等能力、工具与最佳实践。经过近几年发展&#xff0c;Dubbo3 已在阿里巴巴集团各条业务线实现全面…

关于如何进行ChatGPT模型微调的新手指南

微调是指在预训练的模型基础上&#xff0c;通过进一步的训练来调整模型以适应特定任务或领域。预训练的模型在大规模的文本数据上进行了广泛的学习&#xff0c;从中获得了一定的知识和语言理解能力。然而&#xff0c;由于预训练并不针对具体任务&#xff0c;因此需要微调来使模…

深耕全面预算管理 拥抱企业数字未来

随着世界数字未来的不断发展&#xff0c;我国也正经历着一场更大范围、更深层次的科技变革。企业面对构建内部生态平衡体系的艰巨任务&#xff0c;对于其信息化部署也提出了更高的要求。增强预算编制的全面性&#xff0c;启动预算管理一体化改革成为了我国企业提高数字化水平的…

实验3:左右循环LED灯

获取流水灯工程&#xff1a; 方式一&#xff1a; keilproteus 完成最小系统&#xff0c;点亮led 灯实验_吴小凹的博客-CSDN博客 方式二&#xff1a; Flowing_led.zip - 蓝奏云直接下载。 原理图修改&#xff1a; 无须修改只需要使用流水灯的工程即可&#xff0c;解压到桌面…

SQL sever中的索引

目录 一、索引定义 二、索引结构 2.1. B-树索引结构&#xff1a; 2.2. 哈希索引结构&#xff1a; 三、索引作用 四、索引与约束区别 五、索引级别 六、索引分类 6.1. 聚集索引&#xff08;Clustered Index&#xff09;&#xff1a; 6.2. 非聚集索引&#xff08;Noncl…

2023年09月 C/C++(六级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 Python编程&#xff08;1~6级&#xff09;全部真题・点这里 第1题&#xff1a;生日相同 在一个有180人的大班级中&#xff0c;存在两个人生日相同的概率非常大&#xff0c;现给出每个学生的名字&#xff0c;出生月日。试…

redis简介和配置教程

redis简洁版教程 一、概述1、简介2、特点3、优势 二、配置 一、概述 1、简介 Redis是一个高性能的 key-value 数据库。 2、特点 Redis支持数据的持久化&#xff0c;可以将内存中的数据保存在磁盘中&#xff0c;重启的时候可以再次加载进行使用。Redis不仅仅支持简单的key-v…

基于nodejs+vue校园失物招领平台设计与实现

科学技术日新月异的如今&#xff0c;计算机在生活各个领域都占有重要的作用&#xff0c;尤其在信息管理方面&#xff0c;在这样的大背景下&#xff0c;学习计算机知识不仅仅是为了掌握一种技能&#xff0c;更重要的是能够让它真正地使用到实目 录 摘 要 I ABSTRACT II 目 录 II…

【Pytorch】深度学习之损失函数

文章目录 二分类交叉熵损失函数交叉熵损失函数L1损失函数MSE损失函数平滑L1(Smooth L1)损失函数目标泊松分布的负对数似然损失KL散度MarginRankingLoss多标签边界损失函数二分类损失函数多分类的折页损失三元组损失HingEmbeddingLoss余弦相似度CTC损失函数参考资料 学习目标&am…

[Python小项目] 从桌面壁纸到AI绘画

从桌面壁纸到AI绘画 一、前言 1.1 确认问题 由于生活和工作需要&#xff0c;小编要长时间的使用电脑&#xff0c;小编又懒&#xff0c;一个主题用半年的那种&#xff0c;所以桌面壁纸也是处于常年不更换的状态。即时改变主题也是在微软自带的壁纸中选择&#xff0c;而这些自…

1.安装环境

学习Java的第一步应该从配置环境开始&#xff0c;这篇博文介绍了在哪下载安装包以及如何在windows电脑中配置环境&#xff0c;希望大家看完后可以独立安装 ~ 文章目录 一、下载安装包二、 配置环境 一、下载安装包 安装包可以从官网下载&#xff0c;也可以直接私信我拿取。这里…

三、静态路由实验

拓扑图&#xff1a; 两个路由器分了三个网段出来&#xff0c;首先对两台PC机进行配置 进入R1路由器对两边链路进行ip配置 对AR2进行相同的配置&#xff0c;然后我们查看R1的路由表&#xff0c;里面有一些直连的信息。 三个网段的设备现在可以互通&#xff0c;我们要实现跨网段…