vue+springboot实现文件上传

①后端springboot创建controller

FileController:

复制代码
package com.example.springboot.controller;

import cn.hutool.core.io.FileUtil;
import com.example.springboot.common.AuthAccess;
import com.example.springboot.common.Result;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;

@RestController
@RequestMapping("/file")
public class FileController {
    @Value("${ip:localhost}")
    String ip;

    @Value("${server.port}")
    String port;

    private static final String ROOT_PATH=System.getProperty("user.dir")+File.separator+"files";
    @PostMapping("/upload")
    public Result upload(MultipartFile file) throws IOException{
        String originFilename= file.getOriginalFilename();
        String mainName= FileUtil.mainName(originFilename);
        String extName=FileUtil.extName(originFilename);
        if(!FileUtil.exist(ROOT_PATH)){
            FileUtil.mkdir(ROOT_PATH);
        }
        if(FileUtil.exist(ROOT_PATH+File.separator+originFilename)){
            originFilename=System.currentTimeMillis()+"_"+mainName+"."+extName;
        }
        File saveFile=new File(ROOT_PATH+File.separator+originFilename);
        file.transferTo(saveFile);
        String url="http://"+ip+":"+port+"/file/download/"+originFilename;
        return Result.success(url);
    }

    @AuthAccess
    @GetMapping("/download/{filename}")
    public void download(@PathVariable String filename,HttpServletResponse response) throws IOException {
//        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); // 附件下载
        response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(filename, "UTF-8")); // 预览
        String filePath=ROOT_PATH+File.separator+filename;
        if(!FileUtil.exist(filePath)){
            return;
        }
        byte[] bytes=FileUtil.readBytes(filePath);
        ServletOutputStream outputStream=response.getOutputStream();
        outputStream.write(bytes);
        outputStream.flush();
        outputStream.close();
    }
}

②修改yml文件:配置url和port等全局变量

application.yml:

复制代码
server:
  port: 9090

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${ip}:3306/honey2024?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
    username: root
    password: 123456
  servlet:
    multipart:
      max-file-size: 20MB
      max-request-size: 20MB
ip: localhost

③配置全局异常文件

GlobalExeception:

复制代码
package com.example.springboot.exception;

import com.example.springboot.common.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExeception {

    @ExceptionHandler(ServiceException.class)
    @ResponseBody
    public Result serviceException(ServiceException e){
        return Result.error(e.getCode(),e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result globalException(Exception e){
        e.printStackTrace();
        return Result.error("500","系统错误");
    }

}

运行效果:

访问一下这个url,发现可以预览:说明上传功能和预览,下载功能就完成了

至此后端的工作已经完成

④前端修改编写页面

HomeView.vue:

复制代码
<template>
  <div>
    <el-container>
      <!--    侧边栏  -->
      <el-aside :width="asideWidth" style="min-height: 100vh; background-color: #001529">
        <div style="height: 60px; color: white; display: flex; align-items: center; justify-content: center">
          <img src="@/assets/logo1.png" alt="" style="width: 40px; height: 40px">
          <span class="logo-title" v-show="!isCollapse">honey2024</span>
        </div>

        <el-menu :collapse="isCollapse" :collapse-transition="false" router background-color="#001529" text-color="rgba(255, 255, 255, 0.65)" active-text-color="#fff" style="border: none" :default-active="$route.path">
          <el-menu-item index="/">
            <i class="el-icon-menu"></i>
            <span slot="title">系统首页</span>
          </el-menu-item>
          <el-menu-item index="/1">
            <i class="el-icon-house"></i>
            <span slot="title">系统首页</span>
          </el-menu-item>
          <el-menu-item index="/2">
            <i class="el-icon-house"></i>
            <span slot="title">系统首页</span>
          </el-menu-item>
          <el-submenu index="3">
            <template slot="title">
              <i class="el-icon-menu"></i>
              <span>信息管理</span>
            </template>
            <el-menu-item>用户信息</el-menu-item>
            <el-menu-item>管理员信息</el-menu-item>
            <el-menu-item index="/">系统首页</el-menu-item>
          </el-submenu>
        </el-menu>

      </el-aside>

      <el-container>
        <!--        头部区域-->
        <el-header>

          <i :class="collapseIcon" style="font-size: 26px" @click="handleCollapse"></i>
          <el-breadcrumb separator-class="el-icon-arrow-right" style="margin-left: 20px">
            <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
            <el-breadcrumb-item :to="{ path: '/user' }">用户管理</el-breadcrumb-item>
          </el-breadcrumb>

          <div style="flex: 1; width: 0; display: flex; align-items: center; justify-content: flex-end">
            <i class="el-icon-quanping" style="font-size: 26px" @click="handleFull"></i>
            <el-dropdown placement="bottom">
              <div style="display: flex; align-items: center; cursor: default">
                <img src="@/assets/logo1.png" alt="" style="width: 40px; height: 40px; margin: 0 5px">
                <span>管理员</span>
              </div>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item>个人信息</el-dropdown-item>
                <el-dropdown-item>修改密码</el-dropdown-item>
                <el-dropdown-item @click.native="logout">退出登录</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </div>

        </el-header>

        <!--        主体区域-->
        <el-main>
          <div style="box-shadow: 0 0 10px rgba(0,0,0,.1); padding: 10px 20px; border-radius: 5px; margin-bottom: 10px">
            早安,骚年,祝你开心每一天!
          </div>
          <div style="display: flex;">
            <el-card style="width: 30%;margin-right: 10px;">
              <div slot="header" class="clearfix">
                <span>青哥哥带你做毕设2024</span>
              </div>
              <div>
                2024毕设正式开始了!青哥哥带你手把手敲出来!
                <div style="margin-top: 20px">
                  <div style="margin: 10px 0"><strong>主题色</strong></div>
                  <el-button type="primary">按钮</el-button>
                  <el-button type="success">按钮</el-button>
                  <el-button type="warning">按钮</el-button>
                  <el-button type="danger">按钮</el-button>
                  <el-button type="info">按钮</el-button>
                </div>
              </div>
            </el-card>
            <el-card style="width: 70%;">
              <div slot="header" class="clearfix">
                <span>渲染用户的数据</span>
              </div>
              <div>
                <el-table :data="users">
                  <el-table-column label="ID" prop="id"/>
                  <el-table-column label="用户名" prop="username"/>
                  <el-table-column label="姓名" prop="name"/>
                  <el-table-column label="地址" prop="address"/>
                  <el-table-column label="文件上传">
                    <template v-slot="scope">
                      <el-upload
                          action="http://localhost:9090/file/upload"
                          :show-file-list="false"
                          :headers="{token: user.token}"
                          :on-success="(row,file,fileList)=>handleTableFileUpload(scope.row,file,fileList)"
                      >
                        <el-button size="mini" type="primary">点击上传</el-button>
                      </el-upload>
                    </template>
                  </el-table-column>
                  <el-table-column label="文件下载">
                    <template v-slot="scope">
                      <el-image v-if="scope.row.avatar" :src="scope.row.avatar" style="width: 50px;height: 50px"></el-image>
                      <div>
                        <el-button @click="preview(scope.row.avatar)">
                          预览
                        </el-button>
                      </div>
                    </template>
                  </el-table-column>
                </el-table>
              </div>
            </el-card>
          </div>
          <div style="display: flex;margin: 10px 0">
            <el-card style="width: 50%;margin-right: 10px;">
              <div slot="header" class="clearfix">
                <span>文件上传下载</span>
              </div>
              <div>
                <el-upload
                    action="http://localhost:9090/file/upload"
                    list-type="picture"
                    :headers="{token: user.token}"
                    :on-success="handleFileUpload"
                >
                  <el-button size="small" type="primary">单文件上传</el-button>
                </el-upload>
              </div>
              <div style="margin: 10px 0">
                <el-upload
                    action="http://localhost:9090/file/upload"
                    :headers="{token: user.token}"
                    :on-success="handleMutipleFileUpload"
                    multiple
                >
                  <el-button size="small" type="success">多文件上传</el-button>
                </el-upload>
                <el-button @click="showUrls" style="margin: 10px 0" type="primary">显示链接</el-button>
              </div>
            </el-card>
          </div>
        </el-main>

      </el-container>


    </el-container>
  </div>
</template>

<script>
import axios from "axios";
import request from '@/utils/request'

export default {
  name: 'HomeView',
  data() {
    return {
      isCollapse: false,  // 不收缩
      asideWidth: '200px',
      collapseIcon: 'el-icon-s-fold',
      users: [],
      user:JSON.parse(localStorage.getItem('honey-user')||'{}'),
      url:'',
      urls:[]
    }
  },
  mounted() {
    // axios.get('http://localhost:9090/user/selectall').then(res=>{
    //   console.log(res.data);
    //   this.users=res.data.data
    // })

    request.get('/user/selectall').then(res => {
      this.users = res.data
    })
  },
  methods: {
    preview(url){
      window.open(url)
    },
    showUrls(){
      console.log(this.urls)
    },
    handleMutipleFileUpload(response,file,fileList){
      this.urls=fileList.map(v=>v.response?.data)
    },
    handleTableFileUpload(row,file,fileList){
      console.log(row,file,fileList)
      row.avatar=file.response.data
      // this.$set(row,'avatar',file.response.data)
      console.log(row)
      request.put('/user/update',row).then(res=>{
        if(res.code==='200'){
          this.$message.success('上传成功')
        }else{
          this.$message.error(res.msg)
        }
      })
    },
    handleFileUpload(response,file,fileList){
      this.fileList=fileList
      console.log(response,file,fileList)
    },
    logout() {
      localStorage.removeItem("honey-user")
      this.$router.push('/login')
    },
    handleFull() {
      document.documentElement.requestFullscreen()
    },
    handleCollapse() {
      this.isCollapse = !this.isCollapse
      this.asideWidth = this.isCollapse ? '64px' : '200px'
      this.collapseIcon = this.isCollapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'
    }
  }
}
</script>

<style>
.el-menu--inline {
  background-color: #000c17 !important;
}

.el-menu--inline .el-menu-item {
  background-color: #000c17 !important;
  padding-left: 49px !important;
}

.el-menu-item:hover, .el-submenu__title:hover {
  color: #fff !important;
}

.el-submenu__title:hover i {
  color: #fff !important;
}

.el-menu-item:hover i {
  color: #fff !important;
}

.el-menu-item.is-active {
  background-color: #1890ff !important;
  border-radius: 5px !important;
  width: calc(100% - 8px);
  margin-left: 4px;
}

.el-menu-item.is-active i, .el-menu-item.is-active .el-tooltip {
  margin-left: -4px;
}

.el-menu-item {
  height: 40px !important;
  line-height: 40px !important;
}

.el-submenu__title {
  height: 40px !important;
  line-height: 40px !important;
}

.el-submenu .el-menu-item {
  min-width: 0 !important;
}

.el-menu--inline .el-menu-item.is-active {
  padding-left: 45px !important;
}

/*.el-submenu__icon-arrow {*/
/*  margin-top: -5px;*/
/*}*/

.el-aside {
  transition: width .3s;
  box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
}

.logo-title {
  margin-left: 5px;
  font-size: 20px;
  transition: all .3s; /* 0.3s */
}

.el-header {
  box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
  display: flex;
  align-items: center;
}
</style>

最后效果:实现了单文件或者多文件上传和预览下载的功能

相关推荐
架构师沉默14 分钟前
Java 终于有自己的 AI Agent 框架了?
java·后端·架构
程序员爱酸奶14 分钟前
ThreadLocal内存泄漏深度解析
java
givemeacar20 分钟前
Spring Boot中集成MyBatis操作数据库详细教程
数据库·spring boot·mybatis
czlczl2002092523 分钟前
JVM创建对象过程
java·开发语言
前端Hardy28 分钟前
别再手动写 loading 了!封装一个自动防重提交的 Hook
前端·javascript·vue.js
前端Hardy29 分钟前
前端如何实现“无感刷新”Token?90% 的人都做错了
前端·javascript·vue.js
SuperEugene38 分钟前
Vue Router 实战规范:path/name/meta 配置 + 动态 / 嵌套路由,统一团队标准|状态管理与路由规范篇
开发语言·前端·javascript·vue.js·前端框架
一直都在57241 分钟前
线程间的通信
java·jvm
小彭努力中1 小时前
194.Vue3 + OpenLayers 实战:动态位置 + 高度 + 角度,模拟卫星地面覆盖范围
前端·css·vue.js·openlayers·animate
前端Hardy1 小时前
纯 HTML/CSS/JS 实现的高颜值登录页,还会眨眼睛!少女心爆棚!
前端·javascript·vue.js