Spring Boot+VUE分片上传大文件到OSS服务器解决方案

本文介绍了如何在VUE前端和Spring Boot后端结合,实现大文件分片上传到阿里云OSS服务器的解决方案。详细讨论了前后端交互、分片上传的难点以及解决方法,包括使用Redis缓存中间状态,以应对异步上传和多用户隔离的问题。此外,还涵盖了前端组件设计和文件上传进度的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

根据项目需要,在很多地方都需要将超大文件上传到服务器,特别是将视频文件上传到云平台的OSS服务器上,这种需求在项目中已经是十分普遍的需求了。在网上收集了很多资料,基本上都只有JAVA到OSS服务器,或都VUE前端服务端到Spring Boot后端服务器。当前前后端开发已经十分普及了,大文件一般都保存到OSS服务器,不会保存到自己的业务服务器,OSS大文件服务器+tomcat为业务服务器+VUE/Ract/小程序前端服务器的三层后台架构是当前最主流的架构设计,因此必须解决这个问题。
通过几天的反复研究和实践,结合网上找到的资料,需要将VUE服务端到Spring Boot服务器和JAVA到OSS服务器两部分结合起来,形成整体的解决方案。
在这个整体中有两上难点:
1、VUE前端服务器的JS程序是异步的,而大文件分片传输需要大量分次请求,请求执行完所顺序是不一致的;
2、由于是三层请求,涉及到多用户,Spring Boot服务器需要将分原来整体功能,撤分为三个,存在中间缓存信息持久化保存与多用户隔离问题

一、解决方案框架

由于这个功能还是非常复杂,为了解决这个问题,还是绘制了功能框架图,并且在具体编程时,对功能框架图进行了多次修正,最终框架如下图所示。
在这里插入图片描述
1、 框架整体上是前端判断是否大文件,超过10M,如果向Spring Boot服务器,请求大文件传输,Spring Boot向OSS服务器申请创新大文件传递的uploadID.
2、前端对文件进行分片,并产生分片MD5码,根据第1得到的id发起分片传输,Spring Boot收到分片后,上传分片
3、Spring boot在redis中缓存存文件ID,分片信息,以备文件合成用
4、前端检索返回MD5,如果有错,重新传递分片
5、前端检查分片是否全部正确传输完成,如果传递完成,发起文件合并请求
6、Spring boot将缓存信息组合,向OSS提出文件合并请求
7、OSS服务器收到相关信息,检验后合成文件并返回文件访问地址
8、Spring boot将访问地址及相关信息保存到服务器,并清除缓存
9、前端收到信息,显示进展,请除缓存存
10、根据地址使用已经上传的文件,如播放或预览。

二、Spring Boot后台程序

后台程序分为Controller和utils两部分,controller与前端交互,utils与OSS交互。

2.1、前端与用户交互的Controller

由于前后端分离,需要将大文件上传分为三个部分,即大文件上传初始化、上传大文件块、合并已上传文件,分别与VUE前端进行交互。

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author wu jize
 * @since 2020-07-05
 */
@RestController
@RequestMapping("/map/scenic/file")
public class MapBigFileController extends BaseController {
   
    @Autowired
    private IMapScenicFileService iMapScenicFileService;

    @Autowired
    IMapScenicService iMapScenicService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private BigFileUploadUtils bigFileUploadUtils;

    /**
     * 大文件上传初始化
     * 返回文件uploadId
     */
    @ApiOperation("大文件上传初始化")
    @PreAuthorize("@ss.hasPermi('data:scenicfile:edit')")
    @Log(title = "大文件上传初始化", businessType = BusinessType.UPDATE)
    @PostMapping("/initUpload")
    public AjaxResult initBigFileUpload(@RequestParam(name = "fileName", required = true) String fileName,
                                        @RequestParam(name = "scenicId", required = false) Long scenicId) {
   
        //格式化文件路径,按县、景区ID、用户名组织文件
        String adcode = iMapScenicService.selectScenicById(scenicId).getAdcode();
        if (StringUtils.isEmpty(adcode)) {
   
            throw new CustomException("景区行政区域编码为空,不能添加景区多媒体文件!");
        }
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        String author = loginUser.getUsername();
        String objectName = "scenicfile" + "/" + adcode + "/" + scenicId.toString() + "/" + author + '/' + fileName;
        String uploadId = bigFileUploadUtils.initUpload(objectName);

        AjaxResult ajax = AjaxResult.success();
        ajax.put("uploadId", uploadId);
        return ajax;
    }

    /**
     * 上传大文件的Chunk
     * 返回chunk的MD5
     */
    @ApiOperation("上传大文件的Chunk")
    @PreAuthorize("@ss.hasPermi('data:scenicfile:edit')")
    @Log(title = "上传大文件的Chunk", businessType = BusinessType.UPDATE)
    @PostMapping("/uploadChunk")
    public AjaxResult uploadChunk(@RequestParam("chunkFile") MultipartFile chunkFile,
                                  @RequestParam(name = "uploadId", required = true) String uploadId,
                                  @RequestParam(name = "chunkId", required = true) Integer chunkId,
                                  @RequestParam(name = "total", required = true) Integer total) throws IOException {
   

        String md5Str = bigFileUploadUtils.uploadChunk(uploadId, chunkId, chunkFile);
        AjaxResult ajax = AjaxResult.success();

        ajax.put("md5Str", md5Str);
        return ajax;
    }

    /**
     * 大文件上传完成后合并
     * 返回文件访问的URL
     */
    @ApiOperation("大文件上传完成后合并")
    @PreAuthorize("@ss.hasPermi('data:scenicfile:edit')")
    @Log(title = "大文件上传完成后合并", businessType = BusinessType.UPDATE)
    @PostMapping("/mergeFile")
    public AjaxResult mergeFile(@RequestParam(name = "uploadId", required = true) String uploadId) {
   
        AjaxResult ajax = AjaxResult.success();
        String url = bigFileUploadUtils.completeFile(uploadId);
        ajax.put("url", url);
        return ajax;
    }
}

2.2、util包,负责与OSS交互。
在后端与OSS交互中,有一个难点是解决大文件前后端分离后,异步数据上传中该文件相关信息的保存。由于是异步请求,方法分为三段执行,不同请求对于Spring Boot来说就是不同的线程,试了以下几种方法,最后用redis缓存存:
1、用包类静态全局变量,由是不同请求会产生不同线程,此方法行不通
2、用线程变量,静态全局变量不行就考虑过用线程变量,功能实现后现还是不行,进一步研究,发现前端不同请求在后端仍然在不同线程,经如初始化大文件上传的uploadId和objectId,在后面请求访问不到。
3、用session变量,研究了一下,发现更加复杂,还不一定能解决得好并好用户,同一用户不同窗口上传问题
4、反馈给前端,uploadId和objectId还行,但是分片的partETag信息太多,处理起来会十分繁琐
5、用数据库临时表进行持久化,也挺复杂,并且还要引入JPA,如用mybatis工作也不少
6、经过多方比较,还是用redis,简单并且速度快,唯一缺点时redis服务器重启后,没有上传完的片断会变成数据垃圾,还没搞清楚阿里OSS怎么处理,不知道要不要占自己的存储容量。Redis根据uploadId保存信息,不同用户,即使同一用户同时上传不同文件其uploadId是唯一的,这样能做好多用户相互隔离问题。

/**
 * 用于超过10M的大文件分片上传
 *
 * @author Wu Jize
 * @version 1.0
 * @date 2020/7/5 18:28
 */
@Component

public class BigFileUploadUtils {
   
    protected static final Logger log = LoggerFactory.getLogger(OSSFileUtils.class);
	//将OSS访问参数保存到配置文件,并忽略上传GIT,避免有权限的数据泄漏
    @Value("${aliyun.oss.endpoint}")
    private String endpoint;
    
<think>好的,我现在需要帮助用户设计一个基于Spring BootVue的大学生兼职网站的功能模块方案。首先,我需要回顾用户提供的引用内容,看看有没有可用的信息。 用户引用了三个关于Spring Boot兼职系统的资料,其中提到现有的技术如Spring Boot和MySQL,以及可能的功能模块,比如学生端、企业端和管理端。这些引用中提到的主要是系统的技术栈和现有项目的情况,但没有详细的功能模块设计。所以,我需要基于这些技术参考,结合常见的兼职网站需求,来设计功能模块。 接下来,我需要确定主要的功能模块。通常,这类系统会有学生、企业和管理员三个角色。每个角色有不同的需求。学生需要找兼职,管理简历,查看申请状态等;企业需要发布职位,管理招聘信息;管理员则负责审核和系统管理。 然后,考虑技术实现。前端用Vue.js,后端用Spring Boot,数据库用MySQL。需要确保前后端分离,通过RESTful API交互。可能需要用到第三方服务,比如OSS存储图片,短信服务验证登录等。 安全方面,使用JWT进行身份验证和权限控制,确保各模块的数据安全。例如,学生只能修改自己的简历,企业只能管理自己的职位,管理员有最高权限。 接下来,思考每个模块的具体功能点。例如,学生模块包括注册登录、个人信息管理、简历上传、职位搜索与申请、评价等。企业模块需要职位发布、申请管理、学生信息查看等。管理员需要审核企业和职位,处理投诉,监控数据。 此外,需要考虑公共模块,比如首页展示热门职位、搜索功能、通知公告等。这些模块需要前后端协作,后端提供API,前端进行展示和交互。 在实现方案中,需要分层次设计后端,比如Controller处理请求,Service实现业务逻辑,Mapper与数据库交互。前端使用Vue Router管理路由,组件化开发,Axios调用API。数据库设计要合理,比如学生表、企业表、职位表、申请记录表等,并建立适当的关系和索引。 最后,要确保回答结构清晰,分点说明每个模块的功能和实现方式,同时引用提供的资料中的技术栈,比如Spring Boot和MySQL,以及可能的扩展功能如数据分析。这样用户就能全面了解如何设计这样的系统,并参考现有项目进行开发。</think>基于Spring BootVue的大学生兼职网站功能模块设计方案如下: ### 一、系统架构设计 采用前后端分离架构,前端Vue3+Element Plus实现响应式界面,后端Spring Boot 2.7+MyBatis Plus构建RESTful API,MySQL 8.0数据库存储数据[^1][^2] ### 二、核心功能模块设计 #### 1. 学生用户模块 ```mermaid graph TD A[学生模块] --> B[注册/登录] A --> C[个人信息管理] A --> D[简历管理] A --> E[职位搜索/申请] A --> F[申请记录跟踪] A --> G[评价/投诉] ``` - **实现方案**: - 使用JWT实现无状态认证,通过Spring Security配置权限 - 简历存储采用OSS云存储服务,生成简历访问URL - 职位推荐算法:$$ s(i,j) = \sum_{k=1}^{n} w_k \cdot sim(t_k^i, t_k^j) $$ 其中$t_k$表示职位特征维度 #### 2. 企业用户模块 ```mermaid graph TD H[企业模块] --> I[资质认证] H --> J[职位发布] H --> K[申请管理] H --> L[学生档案查看] H --> M[薪酬结算] ``` - **实现方案**: - 企业资质审核采用工作流引擎(如Activiti) - 薪酬结算集成支付宝/微信支付接口 - 使用Elasticsearch实现职位全文检索 #### 3. 管理后台模块 ```mermaid graph TD N[管理模块] --> O[用户审核] N --> P[职位审核] N --> Q[投诉处理] N --> R[数据统计] N --> S[系统配置] ``` - **实现方案**: - 使用ECharts实现数据可视化 - 审核日志记录采用AOP切面编程 - 敏感词过滤使用DFA算法:$$ T = \bigcup_{i=1}^{n} \delta(q_0, w_i) $$ #### 4. 公共模块 - 首页推荐:协同过滤算法实现个性化推荐 - 消息通知:WebSocket实时推送 - 安全验证:阿里云短信服务实现二次验证 ### 三、技术实现方案 #### 后端分层架构: ```java // Controller示例 @RestController @RequestMapping("/api/position") public class PositionController { @Autowired private PositionService positionService; @GetMapping("/search") public Result searchPositions(@RequestParam String keyword) { return Result.success(positionService.search(keyword)); } } ``` #### 前端组件设计: ```vue <template> <el-table :data="positions"> <el-table-column prop="title" label="职位名称"/> <el-table-column label="操作"> <template #default="scope"> <el-button @click="applyPosition(scope.row)">申请</el-button> </template> </el-table-column> </el-table> </template> ``` ### 四、数据库设计要点 1. 学生表:`student(id, name, school, major, ...)` 2. 企业表:`company(id, name, license_no, status, ...)` 3. 职位表:`position(id, title, type, salary, address, ...)` 4. 申请记录表:`application(id, student_id, position_id, status, ...)` ### 五、扩展功能 1. 信用评分系统:基于历史行为动态计算信用分 2. 岗位技能匹配度分析:使用余弦相似度算法 3. 移动端适配:通过Vue3响应式布局实现
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值