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>
相关推荐
在坚持一下我可没意见3 小时前
软件测试入门复习笔记:BUG篇
笔记·bug·测试
云边散步4 小时前
godot2D游戏教程系列二(10)
笔记·学习·游戏·游戏开发
日光倾5 小时前
【Vue.js 入门笔记】闭包和对象引用
前端·vue.js·笔记
今天你TLE了吗6 小时前
JVM学习笔记:第七章——对象实例化、内存布局&访问定位
java·jvm·笔记·学习
悠哉悠哉愿意6 小时前
【物联网学习笔记】串口发送
笔记·物联网·学习
云边散步6 小时前
godot2D游戏教程系列二(9)
笔记·学习·游戏·游戏开发
猹叉叉(学习版)6 小时前
【ASP.NET CORE】 6. 中间件
数据库·笔记·后端·中间件·c#·asp.net·.netcore
Engineer邓祥浩6 小时前
JVM学习笔记(1) 总述
jvm·笔记·学习