标题使用springboot+antd+vue3实现单/多文件、文件夹上传功能
1、先看看后端
先看看我的项目结构吧
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>fileUpload-Antd</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>fileUpload-Antd</name>
<description>fileUpload-Antd</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- 引入sprinbgoot jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 引入sprinbgoot web启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入热部署工具 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- 引入mysql驱动包 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- lombok包 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 引入测试启动器(可有可无) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置application.yml文件
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/files
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
servlet:
multipart:
max-file-size: 300MB
max-request-size: 300MB
devtools:
restart:
enabled: true #设置开启热部署
additional-paths: src/main/java #重启目录
exclude: WEB-INF/**
freemarker:
cache: false #页面不加载缓存,修改即时生效
jpa:
show-sql: true
hibernate:
naming: #命名转化策略 不做任何操作 直接返回数据库中的名称(不做驼峰转化)
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
file:
location: D:/home/uploadfile/ #自定义上传文件本地保存路径
path: /file/ #映射数据库中的文件路径
由于是前后端分离的项目,存在跨域问题,我这边就直接在后端解决跨域问题了,并且一并配置了路径映射问题(也就是数据库存入的文件路径和磁盘真实存储路径映射)
MyWebMVCConfig
package com.example.fileupload.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyWebMVCConfig implements WebMvcConfigurer{
//处理数据库中路径和真实路径的映射问题
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry){
//匹配到resourceHandler,将URL映射至location,也就是本地文件夹
registry.addResourceHandler("/file/**").addResourceLocations("file:///" + "D:/home/uploadfile/");//这里最后一个/不能不写
}
//处理跨域
@Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**")
// .allowedOrigins("*") //浏览器允许所有的域访问 / 注意 * 不能满足带有cookie的访问,Origin 必须是全匹配
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowCredentials(true) // 允许带cookie访问
.allowedHeaders("token")
.maxAge(3600);
}
}
新建实体类Files,用于数据库的操作
package com.example.fileupload.entity;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
@Entity
@Table(name = "files",schema="files")
@Data
public class Files{
@Id
@Column(name = "fileId")
private String FilesId;
@Column(name = "fileName")
private String fileName;
@Column(name = "fileOriginalName")
private String fileOriginalName;
@Column(name = "fileSize")
private Long fileSize;
@Column(name = "fileType")
private String fileType;
@Column(name = "fileExd")
private String fileExd;
@Column(name = "filePath")
private String filePath;
@Column(name = "fileRealPath")
private String fileRealPath;
@Column(name = "cjr")
private String cjr;
@Column(name = "cjsj")
@Temporal(TemporalType.TIMESTAMP)
private Date cjsj;
@Column(name = "scbz")
private String scbz;
}
DAO层,与数据库交互FilesRepoistory
package com.example.fileupload.repository;
import com.example.fileupload.entity.Files;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface FilesRepoistory extends JpaRepository<Files,String>{}
我这边为了方便,没有写service层(其实也没用具体的业务逻辑,直接省略了)
UploadController
package com.example.fileupload.controller;
import com.example.fileupload.entity.Files;
import com.example.fileupload.repository.FilesRepoistory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Controller
public class UploadController{
@Value("${file.location}")
private String location;
@Value("${file.path}")
private String path;
@Autowired
FilesRepoistory filesRepoistory;
@RequestMapping("/list")
@ResponseBody
public List<Files> list(){
List<Files> all = filesRepoistory.findAll();
return all;
}
@RequestMapping("/uploadMultiple")
@ResponseBody
public String upm(HttpServletRequest httpRequest, @RequestParam("files") List<MultipartFile> files){
String res = "fail";
for(MultipartFile file : files){
//获取当前上传文件名称
String fileName = file.getOriginalFilename();
System.out.println("文件名称:" + fileName);
//使用UUID将其重命名
String fileNameUU = UUID.randomUUID().toString();
//截取类型并组合为新名称
String fileNewName = fileNameUU + fileName.substring(fileName.indexOf('.'));
String filePath=this.path+fileNewName;
String fileRelPath=this.location + fileNewName;
System.out.println("文件新名称:" + fileNewName);
try{
file.transferTo(new File(fileRelPath));
Files files1 = new Files();
files1.setFilesId(UUID.randomUUID().toString());
files1.setFileName(fileNewName);
files1.setFileOriginalName(fileName);
files1.setFileSize(file.getSize());
files1.setFileType(file.getContentType());
files1.setFileExd(fileName.substring(fileName.indexOf('.')));
files1.setFilePath(filePath);
files1.setFileRealPath(fileRelPath);
files1.setCjr("Junibl");
files1.setCjsj(new Date());
files1.setScbz("N");
res="ok";
this.filesRepoistory.save(files1);
}catch(IOException e){
e.printStackTrace();
}
}
return res;
}
//上传单个文件
@RequestMapping("/uploadSingle")
@ResponseBody
public String ups(HttpServletRequest httpRequest, @RequestParam("file") MultipartFile file){
String res = "fail";
//获取当前上传文件名称
String fileName = file.getOriginalFilename();
System.out.println("文件名称:" + fileName);
//使用UUID将其重命名
String fileNameUU = UUID.randomUUID().toString();
//截取类型并组合为新名称
String fileNewName = fileNameUU + fileName.substring(fileName.indexOf('.'));
String filePath=this.path+fileNewName;
String fileRelPath=this.location + fileNewName;
System.out.println("文件新名称:" + fileNewName);
try{
file.transferTo(new File(fileRelPath));
Files files1 = new Files();
files1.setFilesId(UUID.randomUUID().toString());
files1.setFileName(fileNewName);
files1.setFileOriginalName(fileName);
files1.setFileSize(file.getSize());
files1.setFileType(file.getContentType());
files1.setFileExd(fileName.substring(fileName.indexOf('.')));
files1.setFilePath(filePath);
files1.setFileRealPath(fileRelPath);
files1.setCjr("Junibl");
files1.setCjsj(new Date());
files1.setScbz("N");
res="ok";
this.filesRepoistory.save(files1);
}catch(IOException e){
e.printStackTrace();
}
return res;
}
}
其实上面的三个方法可以合并,我就懒得处理了
启动器FileUploadAntdApplication,这就没啥可说的了
package com.example.fileupload;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FileUploadAntdApplication{
public static void main(String[] args){
SpringApplication.run(FileUploadAntdApplication.class, args);
}
}
前端
前端要细讲还挺多的,但是离不开nodejs,按照官方给的文档新建项目就行
1、下载nodejs 安装,使用node -v查看是否安装成功(注意版本:我这边是16+)
2、安装pnpm 用于管理包,原生的npm浪费内存资源,使用pnpm -v查看是否安装成功(反正尽量都安装最新的就行)
3、去vue官网,按照教程安装vue,查看是否安装成功vue -V(其实指的是脚手架)
跟着教程创建项目
好了,进入教程安装依赖包:pnpm i
,等待即可
安装antd-vue:pnpm install ant-design-vue --save
,等待
安装axios:pnpm install axios
,等待
安装qs(JSON处理的一个工具):pnpm i qs
,继续等待,所有安装好了就差不多了
全局引入vue和antd,main.ts(我这边是加入了typescript,所以是main.ts)
import { createApp } from 'vue'
import App from './App.vue'
import 'ant-design-vue/dist/antd.css';
import Antd from "ant-design-vue";
import './assets/main.css'
const app=createApp(App)
app.use(Antd).mount('#app')
app.config.productionTip = false
我想偷懒,所以直接在app.vue上写东西了App.vue
<script setup lang="ts">
import axios, { type AxiosProgressEvent } from "axios";
import { ref } from "vue";
//上传进度
let pre=ref()
let preFolder=ref(0)
let preMul=ref(0)
const uploadMultifile = async (option: Request) => {
const callBack=updateProcess
const formData = new FormData()
const fileUrl = "http://localhost:8080/uploadMultiple"
formData.append("files", option.file)
// console.log(option.file)
let that=this
axios.post(
fileUrl,
formData,
{
onUploadProgress:(progressEvent:AxiosProgressEvent)=>{
// 处理原生进度事件
// console.log(progressEvent)
callBack && callBack(progressEvent,'mul')
}
}
)
.then((res)=>{
// console.log('上传结果:',res)
option.onSuccess(res.data, option)
})
};
const uploadFolder =async (option: Request) => {
const callBack=updateProcess
const formData = new FormData()
const fileUrl = "http://localhost:8080/uploadMultiple"
formData.append("files", option.file)
// console.log(option.file)
let that=this
axios.post(
fileUrl,
formData,
{
onUploadProgress:(progressEvent:AxiosProgressEvent)=>{
// 处理原生进度事件
// console.log(progressEvent)
callBack && callBack(progressEvent,'folder')
}
}
)
.then((res)=>{
// console.log('上传结果:',res)
option.onSuccess(res.data, option)
})
};
const uploadSingle = async (option: Request) => {
const callBack=updateProcess
const formData = new FormData()
const fileUrl = "http://localhost:8080/uploadSingle"
formData.append("file", option.file)
axios.post(
fileUrl,
formData,
{
onUploadProgress:(progressEvent:AxiosProgressEvent)=>{
// 处理原生进度事件
// console.log(progressEvent)
callBack && callBack(progressEvent,'single')
}
}
)
.then((res)=>{
// console.log('上传结果:',res)
option.onSuccess(res.data, option)
})
}
//更新进度条
const updateProcess=function(progressEvent:AxiosProgressEvent,type: string):void{
// console.log('progressEvent:',progressEvent)
if(type=='folder'){
preFolder.value=Math.floor(progressEvent.loaded/progressEvent.total*100)
}else if(type=='single'){
pre.value=Math.floor(progressEvent.loaded/progressEvent.total*100)
}else if(type=='mul'){
preMul.value=Math.floor(progressEvent.loaded/progressEvent.total*100)
}
}
</script>
<template>
<a-row type="flex">
<a-col :span="8">
上传文件夹:
<a-upload directory :customRequest="uploadFolder">
<a-button type="primary">上传文件夹 </a-button>
</a-upload>
<a-progress :percent="preFolder" status="active" />
</a-col>
<a-col :span="8">
上传多个文件:
<a-upload :multiple="true" :customRequest="uploadMultifile">
<a-button type="primary">上传多文件 </a-button>
</a-upload>
<a-progress :percent="preMul" status="active" />
</a-col>
<a-col :span="8">
上传单个文件:
<a-upload :customRequest="uploadSingle" >
<a-button type="primary">上传单个文件 </a-button>
</a-upload>
<a-progress :percent="pre" status="active" />
</a-col>
</a-row>
</template>
<style scoped>
</style>
好了基本OK了,来看看效果吧