Spring Boot + Vue 实现文件上传下载

在Web应用开发中,文件上传和下载是非常常见的功能,例如用户头像上传、附件管理、图片展示等。本文将通过一个完整的示例,介绍如何使用Spring Boot 构建后端文件上传下载接口,并结合Vue 3 + Element Plus前端组件实现文件的上传与展示。代码简洁清晰,可直接应用于实际项目。

技术栈

  • 后端:Spring Boot 2.x,文件操作使用java.nio.file

  • 前端:Vue 3 + Element Plus(el-upload、el-image)

  • 构建工具:Maven(后端),Vite(前端)

功能概述

  1. 上传文件:通过POST请求将文件保存到服务器指定目录,并返回该文件的下载URL。

  2. 下载文件:通过GET请求根据文件名返回文件流(支持浏览器直接下载或预览)。

  3. 前端集成:使用Element Plus的上传组件上传头像,上传成功后展示图片。

后端实现

1. 创建Spring Boot项目

pom.xml中添加必要依赖:

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. 编写文件上传下载Controller

创建FileController,处理/api/files/upload(上传)和/api/files/download/{fileName}(下载)请求。

java 复制代码
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@RestController
@RequestMapping("/api/files")
public class FileController {

    // 文件存储目录,实际项目中应配置在application.yml中
    private final String uploadDir = "uploads/";

    public FileController() {
        // 确保上传目录存在
        Path path = Paths.get(uploadDir);
        if (!Files.exists(path)) {
            try {
                Files.createDirectories(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 文件上传
     * @param file 上传的文件(参数名必须与前端一致)
     * @return 文件的下载URL
     */
    @PostMapping("/upload")
    public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            String fileName = file.getOriginalFilename();
            Path path = Paths.get(uploadDir + fileName);
            Files.write(path, file.getBytes());

            // 构建可访问的下载URL
            String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                    .path("/api/files/download/")
                    .path(fileName)
                    .toUriString();

            return ResponseEntity.ok(fileDownloadUri);
        } catch (IOException e) {
            return ResponseEntity.badRequest().body("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 文件下载
     * @param fileName 文件名
     * @param response HttpServletResponse
     */
    @GetMapping("/download/{fileName}")
    public void download(@PathVariable String fileName, HttpServletResponse response) throws IOException {
        Path path = Paths.get(uploadDir + fileName);

        if (!Files.exists(path)) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 设置响应头
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

        byte[] bytes = Files.readAllBytes(path);
        ServletOutputStream os = response.getOutputStream();
        os.write(bytes);
        os.flush();
        os.close();
    }
}

关键点说明

  • 上传接口使用MultipartFile接收文件,保存到本地uploads/目录(若目录不存在则自动创建)。

  • 返回的URL是通过ServletUriComponentsBuilder动态生成的绝对路径,确保前端可以直接访问下载接口。

  • 下载接口设置Content-Typeapplication/octet-stream,强制浏览器以附件形式下载;若希望图片直接显示,可改为对应的MIME类型(如image/jpeg),下文会讨论优化方案。

前端实现(Vue 3 + Element Plus)

1. 引入Element Plus

在Vue项目中安装Element Plus并全局引入(或按需引入):

bash 复制代码
npm install element-plus

main.js中:

javascript 复制代码
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'

const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

2. 表单中使用el-upload上传头像

假设有一个用户表单,包含头像上传项:

XML 复制代码
<template>
  <el-form :model="form" label-width="80px">
    <el-form-item label="头像">
      <el-upload
        action="http://localhost:8080/api/files/upload"
        :on-success="handleFileSuccess"
        list-type="picture"
      >
        <el-button type="primary">上传头像</el-button>
      </el-upload>
    </el-form-item>

    <!-- 其他表单项... -->
  </el-form>
</template>

<script setup>
import { reactive } from 'vue'

const form = reactive({
  avatar: '' // 用于存储头像URL
})

const handleFileSuccess = (response) => {
  // response 是上传成功后后端返回的字符串(即下载URL)
  form.avatar = response
  console.log('头像URL:', response)
}
</script>

说明

  • action指向后台上传接口地址,需与后端实际地址一致(注意端口)。

  • on-success回调在文件上传成功后触发,response参数即为后端返回的字符串(图片下载URL),将其保存到表单字段中。

3. 在表格中展示头像

若需要在表格中展示已上传的头像,可以使用el-image组件,src绑定为保存的URL:

XML 复制代码
<el-table :data="tableData">
  <el-table-column label="头像" width="120">
    <template #default="scope">
      <el-image
        v-if="scope.row.img"
        :src="scope.row.img"
        :preview-src-list="[scope.row.img]"
        :preview-teleported="true"
        style="width: 40px; height: 40px; border-radius: 50%; display: block"
      />
    </template>
  </el-table-column>
  <!-- 其他列 -->
</el-table>
相关推荐
Flittly21 小时前
【SpringAIAlibaba新手村系列】(11)Embedding 向量化与向量数据库
java·笔记·spring·ai·springboot
热爱生活的猴子1 天前
Tokenizer 与 Embedding 核心笔记
笔记·embedding
杰尼龟3681 天前
Convince Develop 学习笔记
笔记·学习
不早睡不改名@1 天前
Netty源码分析---Reactor线程模型深度解析(二)
java·网络·笔记·学习·netty
2501_938176881 天前
股指期货的交易成本全解析
笔记
中屹指纹浏览器1 天前
2026多账号运营的零信任架构:指纹浏览器与网络安全的深度融合实践
经验分享·笔记
热爱生活的猴子1 天前
训练与推理时 Tokenizer Padding 用法笔记
人工智能·笔记·机器学习
Dr.F.Arthur1 天前
我的算法笔记——哈希表篇
数据结构·笔记·散列表
ZhiqianXia1 天前
Pytorch 学习笔记(4) : torch.backends
pytorch·笔记·学习
FakeOccupational1 天前
【电路笔记 通信】8B_10B编码 高速数据传输的串行数据编码技术 论文流程对应实现(简化版本,仅编码数值)
笔记