作者: Memory(星哥) Wechat:/QQ: 574373426
整理不易,感谢支持,欢迎 收藏 转发 分享
专注IT职业教育多年,学编程找星哥
SpringBoot+Vue+ElementUI实现文件拖拽点击上传,文件下载
前言
技术栈
前后端分离
后端: SpringBoot, IO流
前端: Vue , ElementUI , Axios
开发工具: IDEA2020.3.4
引言
文件上传,和下载在我们的大部分项目中都比较适用
比如: 学生提交作业,毕业设计,员工提交报销发票凭证等
本文章实现了文件拖拽或者点击上传,点击下载功能,方便大家在做相关业务的时候能快速上手,效果图如下
前端代码
使用 ElementUI 的文件上传组件,很方便
使用Vue做数据,事件绑定
重要:
业务逻辑: 在上传文件的时候在前端直接判断文件格式是否正确,我这里只判断了部分(jpg,png,pdf,xls等) 使用 file.type 这个属性做判断 如果大家不知道 某一个格式在浏览器解析时候前端的具体格式,可以上传文件触发事件时先 alert(file.type) 输出一下,就知道该如何判断了,例如PDF格式是这样的: file.type === 'application/pdf’
注意:代码里因为下载演示比较麻烦,没有实际业务数据,我下载文件地址动态的设置了上一次上传文件的路径,所以要先上传文件,再点击下载
ElementUI上传组件参数解释:
官方文档描述: https://element.eleme.cn/#/zh-CN/component/upload
我用到的:
action 必选参数,上传的地址
drag 支持拖拽上传
multiple 支持多选文件(前台支持,我后台代码没支持,略微麻烦可以自己尝试)
:file-list 显示已上传列表
:on-success 文件上传成功时的钩子函数(上传成功走此方法)
:before-upload 上传文件之前的钩子,参数为上传的文件,用来做判断(上传前先走此方法)
其他可以参考文档根据实际业务添加
代码如下: 采用cdn方式在线引入,保证电脑有网
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<body>
<!-- Vue开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 引入element样式element -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入element组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<!-- 引入axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="app">
<h2>文件上传</h2>
<el-upload
class="upload-demo"
drag
action="http://localhost:8080/upload"
multiple
:file-list="fileList"
:on-success="handleSucess"
:before-upload="beforeAvatarUpload">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传jpg/png/txt/pdf/excel文件,且不超过2MB</div>
</el-upload>
<h2>文件下载</h2>
<p>模拟下载,假设下面是某个文件列表</p>
<div @click="downFile"><i class="el-icon-document"></i>娱乐圈不为人知的秘密.pdf</div>
</div>
<script>
new Vue({
el: "#app",
data() {
return {
fileList: [],
fileinfo:{
virtualPath: ""
}
}
},
methods: {
beforeAvatarUpload(file) {
//alert(file.type)
const isJPG = file.type === 'image/jpeg';
const isPNG = file.type === 'image/png';
const isPDF = file.type === 'application/pdf';
const isXLSX = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
const isXLS = file.type === 'application/vnd.ms-excel'
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG && !isPNG && !isPDF && isXLSX && isXLS) {
this.$message.error('上传文件格式只能是 JPG PNG TXT PDF XLS XLSX格式!');
}
if (!isLt2M) {s
this.$message.error('上传头像图片大小不能超过 2MB!');
}
return (isJPG || isPNG || isPDF || isXLSX || isXLS) && isLt2M;
},
handleSucess(result) {
alert("上传成功")
console.log("存储路径:"+result.virtualPath)
console.log("文件名:"+result.fileName)
this.fileinfo.virtualPath=result.virtualPath
},
downFile(){
alert("开始下载")
//动态获取刚刚上传的文件的路径,所以必须先上传,再下载,当然你也可以把路径写死
//实际项目中,获取目标文件路径即可,这个不是重点
console.log(this.fileinfo.virtualPath)
var url = "http://localhost:8080/downloadFile?filePath="+this.fileinfo.virtualPath
//这里直接使用window.open 发起请求在新页面显示返回的内容,也可以使用axios,这样比较简单,效果类似
window.open(url)
}
}
})
</script>
</body>
</html>
后台项目搭建
创建项目
采用阿里云脚手架创建SpringBoot项目: https://start.aliyun.com
勾选web和lombok依赖,也可以创建项目后手动添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
创建配置文件 image.properties
此配置文件配置上传文件的地址路径,你要想写死我也不拦你
在resource下,新建properties目录
在resource/properties目录下创建image.properties
#配置windows服务器路径
image.localPathDir=D:/files
#配置Linux服务器路径 我这是Mac系统 在业务中选择下面这个地址
image.localUrlPath=/Users/zhaoguoxing/Desktop/files
application.yml配置文件
springboot配置文件,只配个端口号即可
# 应用服务 WEB 访问端口
server:
port: 8080
上传文件实现
实体类
在com.gx.vo下 创建FileVO
用于封装 文件路径以及文件名,返回给前端
我业务逻辑中没用到文件名,后期可扩展
使用lombok插件 帮我们自动生成构造方法和 get set方法
package com.gx.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class FileVO {
private String virtualPath; //动态变化的路径
private String fileName; //文件名称 uuid.pdf
}
控制层
在com.gx.controller下创建FileController用于接收请求
package com.gx.controller;
import com.gx.service.FileService;
import com.gx.vo.FileVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
@RestController
@CrossOrigin
public class FileController {
@Autowired
FileService fileService;
@PostMapping("/upload")
public FileVO upload(MultipartFile file) throws IOException {
System.out.println("upload--file is"+file);
//调用service中的业务方法
FileVO fileVO = fileService.upload(file);
return fileVO;
}
}
业务层
在com.gx.service下创建FileService以及FileServiceImpl
FileService
package com.gx.service;
import com.gx.vo.FileVO;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.OutputStream;
public interface FileService {
//文件上传
public FileVO upload(MultipartFile file) throws IOException;
}
FileServiceImpl
思路: 获取文件 重新定义文件名 以及文件存储路径 将文件保存到目录
这里没有连接数据库,我们可以将图片存储的地址实际放到数据库中存储
package com.gx.service;
import com.gx.vo.FileVO;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@Service
//加载指定的配置文件
@PropertySource("classpath:/properties/image.properties")
public class FileServiceImpl implements FileService{
//获取配置文件中的配置 为属性动态赋值 注解@Value
@Value("${image.localPathDir}")
private String localPathDir; // Windows路径 例如 D:/files
@Value("${image.localUrlPath}")
private String localUrlPath; // Linux路径 例如 /Users/zhaoguoxing/Desktop/files
@Override
public FileVO upload(MultipartFile file) throws IOException {
//1.1 获取文件名称
String fileName = file.getOriginalFilename();
//2. 目录结构
//2.1 实现分目录存储 可以以时间维度年月日进行分隔 /yyyy/MM/dd/
String datePath =
new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
//2.2 最终本地图片存储路径
// 进行目录的拼接 "/Users/zhaoguoxing/Desktop/files/2022/03/22";
String localDir = localUrlPath + datePath;
//2.3 需要创建目录
File dirFile = new File(localDir);
if(!dirFile.exists()){
dirFile.mkdirs();
}
//3.文件名称重复 采用UUID防止文件重名 uuid.pdf
String uuid = UUID.randomUUID().toString()
.replace("-", "");
//3.1.获取文件类型
//fileName = abc.jpg fileType=.pdf
String fileType =
fileName.substring(fileName.lastIndexOf("."));
//3.2.重新拼接文件名 uuid.pdf
String realFileName = uuid + fileType;
//4.最终文件存储的路径+文件名 = /2021/11/11/uuid.pdf
//可以在这里将路径存储到数据库 实际保存文件地址 此处省略
String filePathAll = localDir + realFileName;
//5.实现文件上传
File realFile = new File(filePathAll);
file.transferTo(realFile);
//6.封装FileVO对象 //2021/11/11/uuid.pdf 图片路径 稍后给前台传递
//我们不可能将filePathAll告诉用户,这样不安全,容易被攻击
//virtualPath 半个路径,没有具体盘符或根目录 /2021/11/11/uuid.pdf
String virtualPath = datePath + realFileName;
//7.将文件存储路径(半个路径,没有具体盘符或根目录) 和 重命名后的文件名 封装到实体类中
return new FileVO(virtualPath,realFileName);
}
}
效果
前台可以拖拽或者点击选择文件上传
前端显示已上传文件历史,并在控制台输出了文件路径
这个路径肯定不能给用户返回实际带根目录的路径,不安全
根目录我们在后台动态拼接即可
实际存储到目录中
下载文件实现
控制层
在FileController中添加方法接收下载请求
@GetMapping("/downloadFile")
public Object downloadFile(HttpServletResponse response,String filePath) throws IOException {
// 清空输出流
response.reset();
response.setContentType("application/x-download;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename="+ new String(filePath.getBytes("utf-8"), "utf-8"));
fileService.download(response.getOutputStream(),filePath);
return null;
}
业务层
在FileService和FileServiceImpl中添加下载方法
FileService
public Object download(OutputStream os,String filePath) throws IOException;
FileServiceImpl
@Override
public Object download(OutputStream os, String filePath) throws IOException {
//下载文件的路径
String downPath = localUrlPath+filePath;
//读取目标文件
File f = new File(downPath);
//创建输入流
InputStream is = new FileInputStream(f);
//做一些业务判断,我这里简单点直接输出,你也可以封装到实体类返回具体信息
if (is == null) {
System.out.println("文件不存在");
}
//利用IOUtils将输入流的内容 复制到输出流
//org.apache.tomcat.util.http.fileupload.IOUtils
//项目搭建是自动集成了这个类 直接使用即可
IOUtils.copy(is, os);
os.flush();
is.close();
os.close();
return null;
}
效果
点击娱乐圈不为人知的秘密.pdf开始下载 最后一次上传的文件
注意:
因为下载业务没有数据支持,我代码里下载文件设置的是上一次上传文件的路径,来演示效果,实际业务中,获取目标文件件路径即可
gitee代码仓库地址
https://gitee.com/memoryzgx/file-upload.git
欢迎转发,收藏,点赞
欢迎转发,收藏,点赞
欢迎转发,收藏,点赞