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

news2025/2/26 8:21:46

引言

文件上传是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桶的文件名。后面后端展示逻辑有显示,实际根据自己需求调整。

<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() {
        // 使用时间戳来生成唯一ID
        const 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.message
              console.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;
    }
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(uploadProgressHandler, "/test/upload-progress").setAllowedOrigins("*").addInterceptors(new HttpSessionHandshakeInterceptor());
    }

    /**
     * 引入定时任务bean,防止和项目中quartz定时依赖冲突
     */
    @Bean
    @Nullable
    public 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<>();

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        uploadProgressMap.forEach((requestId, progressListener) -> {
            try {
                session.sendMessage(new TextMessage(progressListener.getProgress()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
    @Override
    public 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%";
    }

    @Override
    public 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 {
 @Autowired
    private 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服务未连接!");
        }
        // 关闭除最后一个之外的其他WebSocketSession
        for (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.coloradmin.cn/o/1087647.html

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

相关文章

开源在线客服系统源码微信小程序

又来啦&#xff01;今天要给大家分享的是一款在线客服微信小程序源码系统&#xff0c;在外面现在的日常生活中&#xff0c;客服是不可或缺的岗位&#xff0c;下面我们一起来看看这款系统的功能介绍吧。下面是部分的代码截图&#xff1a; 在线客服系统源码微信小程序的功能主要包…

多场景通吃,INDEMIND视觉导航方案赋能服务机器人更多可能

打破场景限制&#xff0c;不一样的“斜杠青年”。 随着服务机器人不断进入到商场、超市、写字楼、酒店等新场景&#xff0c;场景的多样化和复杂度也在明显提升&#xff0c;由于场景的独特性&#xff0c;对于机器人的要求也千差万别&#xff0c;这意味机器人需要更强大的适应性…

1.go web之gin框架

Gin框架 一、准备 1.下载依赖 go get -u github.com/gin-gonic/gin2.引入依赖 import "github.com/gin-gonic/gin"3. &#xff08;可选&#xff09;如果使用诸如 http.StatusOK 之类的常量&#xff0c;则需要引入 net/http 包 import "net/http"二、基…

css 星星闪烁加载框

今天带来的是普灵普灵的loader闪烁加载框 效果如下 开源精神给我们带来了源码 ,源码如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, in…

LangChain的发展史

LangChain 发展史 LangChain 的作者是 Harrison Chase&#xff0c;最初是于 2022 年 10 月开源的一个项目&#xff0c;在 GitHub 上获得大量关注之后迅速转变为一家初创公司。2017 年 Harrison Chase 还在哈佛上大学&#xff0c;如今已是硅谷的一家热门初创公司的 CEO&#xf…

绘制X-Bar-S和X-Bar-R图,监测过程,计算CPK过程能力指数

X-Bar-S图和X-Bar-R图是统计质量控制中常用的两种控制图&#xff0c;用于监测过程的稳定性和一致性。它们的主要区别在于如何计算和呈现数据的变化以及所关注的问题类型。 X-Bar-S图&#xff08;平均值与标准偏差图&#xff09;&#xff1a; X-Bar代表样本均值&#xff0c;S代表…

制造业中CRM系统的作用有哪些

CRM系统是企业信息化建设的一部分&#xff0c;制造企业要想在疫情残酷的市场环境中生存下去离不开客户资源&#xff0c;因此我们说制造业CRM系统是当下企业数字化、信息化的关键。制造业CRM系统的作用有哪些?一起来看看吧。 让销售过程更规范 制造业需要CRM系统规范化销售流…

2023年中国饲料酸化剂产量、需求量及市场规模分析[图]

饲料酸化剂又称酸度调节剂&#xff0c;作为新型的饲料添加剂&#xff0c;其作用是保持动物体内的电解质平衡&#xff0c;促进动物健康生长。饲料酸化剂的众多作用机理都源于其酸化效应&#xff0c;即通过降低消化道中pH值使影响动物生长、生产所需的一些相关因素得以很好地发挥…

leetcodeT912-快排优化-三路划分

leetcodeT912-快排优化-三路划分 1.前言2.为什么需要三路划分的优化?3.三路划分的思想及举例画图4.三路划分的代码实现5.三数取中修改 1.前言 因为快排的名声太大 并且快排在某些场景下比较慢,所以leetcode"修理"了一下快排 特意设计了一些专门针对快排的测试用例 所…

Windows 程序注册服务运行

文章目录 创建服务窗口功能AppilicationDetailsLog OnDependencies 编辑服务删除服务服务管理进程信息运行状态 可以使用 nssm、 easy-service 来管理&#xff0c;本文使用 NSSM 来操作 创建服务 使用 nssm install [<servicename>] 调出 UI 窗口&#xff0c;第三个参…

VS 断点调试技巧:直接看到成员变量,隐藏属性,跳过方法

问题描述 直接断点不能直接显示成员变量 可以改下ToString方法&#xff0c;但这个方法可能还有其他用途&#xff0c;不推荐 解决方法 类加标签&#xff1a;DebuggerDisplay 可以直接显示属性了&#xff0c;但是有密码私密属性或者不重要的属性&#xff0c;怎么隐藏呢 隐藏属性…

快手商品详情数据接口,快手商品详情API接口,快手API接口

在网页抓取方面&#xff0c;可以使用 Python、Java 等编程语言编写程序&#xff0c;通过模拟 HTTP 请求&#xff0c;获取快手网站上的商品页面。在数据提取方面&#xff0c;可以使用正则表达式、XPath 等方式从 HTML 代码中提取出有用的信息。值得注意的是&#xff0c;快手网站…

【k8s 开发排错】k8s组件开发排错之pprof

参考 Kubernetes组件问题排查的一些方法 - 知乎 go 程序性能调优 pprof 的使用 &#xff08;一&#xff09; - 润新知 Go进阶系列 之 性能分析神器pprof__好吗_好的的博客-CSDN博客 k8s各组件端口_k8s10259端口-CSDN博客 Go调试神器pprof使用教程【实战分享】_NPE~的博客-C…

Datawhale发布,首个AI Prompt 工程师计划!

Datawhale发布 联合&#xff1a;讯飞星火、开源学习高校联盟 无论你是新手还是有 AI 基础 只要你对 AI 方向感兴趣、有热情 Datawhale AI Prompt 工程师计划 联合科大讯飞星火大模型 面向在校学生、社会在职人士 提供 AI Prompt 的免费学习机会 通过考试可获官方颁发的 Promp…

《面试求职系列》(三):如何写简历及求职技巧

&#x1f499;个人主页: GoAI |&#x1f49a; 公众号: GoAI的学习小屋 | &#x1f49b;交流群: 704932595 |&#x1f49c;个人简介 &#xff1a; 掘金签约作者、百度飞桨PPDE、领航团团长、开源特训营导师、CSDN、阿里云社区人工智能领域博客专家、新星计划计算机视觉方向导师…

周四见|物流人的一周资讯

顺丰铁联多式联运平台示范工程被命名为“国家多式联运示范工程” 10月10日消息&#xff0c;交通运输部、国家发展改革委近日联合印发通知&#xff0c;决定命名中欧班列集装箱多式联运信息集成应用示范工程等19个项目为“国家多式联运示范工程”。其中&#xff0c;顺丰铁联多式…

如何测量GNSS信号和高斯噪声功率及载波比?

引言 本文将介绍如何测量德思特Safran GSG-7或GSG-8 GNSS模拟器的输出信号功率。此外&#xff0c;还展示了如何为此类测量正确配置德思特Safran Skydel仿真引擎以及如何设置射频设备&#xff0c;从而使用频谱分析仪准确测量信号的射频功率。 什么是载波噪声密度C/N0 GNSS接收…

OpenSM-QoS管理

目录 一、概述 二、完整QoS策略文件 三、简化QoS策略定义 四、策略文件语法规则 五、完整策略文件示例 六、简化的QoS策略 6.1 IPoIB 6.2 SDP 6.3 RDS 6.4 iSER 6.5 SRP 6.6 MPI 七、SL2VL映射和VL仲裁 缩写全拼中文SMSubnet Manager子网管理器&#xff08;实体&a…

精品Python的美食餐饮服务系统点餐订餐-可视化大屏

《[含文档PPT源码等]精品Python的美食餐饮服务系统的设计与实现-爬虫》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程等&#xff01; 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;python 使用框架&#xff1a;Django 前端技术&#x…

学习开发一个RISC-V上的操作系统(汪辰老师) — 一次RV32I加法指令的反汇编

前言 &#xff08;1&#xff09;此系列文章是跟着汪辰老师的RISC-V课程所记录的学习笔记。 &#xff08;2&#xff09;该课程相关代码gitee链接&#xff1b; &#xff08;3&#xff09;PLCT实验室实习生长期招聘&#xff1a;招聘信息链接 前置知识 RISC-V 汇编指令编码格式 &a…