SpringBoot+Vue+ElementUI实现文件上传与文件下载

作者: Memory(星哥) Wechat:/QQ: 574373426
整理不易,感谢支持,欢迎 收藏 转发 分享
专注IT职业教育多年,学编程找星哥

前言

技术栈

前后端分离
后端: 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

欢迎转发,收藏,点赞
欢迎转发,收藏,点赞
欢迎转发,收藏,点赞

实现文件上传需要完成以下几个步骤: 1. 在前端页面添加上传文件的表单,并绑定上传事件,将文件上传到后端。 2. 在后端接收前端上传的文件,并保存到服务器上。 下面是一个简单的示例,演示如何使用 Spring Boot + Vue.js + ElementUI 实现文件上传的功能。 前端代码: ```html <template> <div> <el-upload class="upload-demo" ref="upload" :action="uploadUrl" :on-success="handleSuccess" :before-upload="beforeUpload" :file-list="fileList"> <el-button size="small" type="primary">点击上传</el-button> <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div> </el-upload> </div> </template> <script> export default { data() { return { uploadUrl: "/upload", fileList: [] }; }, methods: { // 上传前的钩子函数 beforeUpload(file) { const isJPG = file.type === "image/jpeg" || file.type === "image/png"; const isLt500K = file.size / 1024 < 500; if (!isJPG) { this.$message.error("上传头像图片只能是 JPG/PNG 格式!"); } if (!isLt500K) { this.$message.error("上传头像图片大小不能超过 500KB!"); } return isJPG && isLt500K; }, // 上传成功的回调函数 handleSuccess(response, file, fileList) { this.fileList = fileList; this.$emit("upload-success", response); } } }; </script> ``` 在上面的代码中,我们使用了 ElementUI 的 Upload 组件,将上传文件的表单渲染到页面上。同时,我们还绑定了 beforeUpload 和 handleSuccess 两个事件,用于上传前的校验和上传成功后的回调。 注意:在使用 beforeUpload 钩子函数时,需要返回一个布尔值,表示是否允许上传。如果返回 false,上传将会被终止。 后端代码: ```java @RestController public class FileUploadController { @PostMapping("/upload") public String uploadFile(@RequestParam("file") MultipartFile file) throws Exception { if (file.isEmpty()) { return "上传文件为空!"; } // 获取文件名 String fileName = file.getOriginalFilename(); // 获取文件的后缀名 String suffixName = fileName.substring(fileName.lastIndexOf(".")); // 设置文件存储路径 String filePath = "/tmp/"; // 重新生成文件名 fileName = UUID.randomUUID() + suffixName; // 创建文件对象 File dest = new File(filePath + fileName); // 检测是否存在目录 if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } // 保存文件 file.transferTo(dest); return "文件上传成功!"; } } ``` 在上面的代码中,我们使用了 Spring Boot 的 @PostMapping 注解,将上传文件的接口地址映射到 /upload 路径上。同时,我们还使用了 MultipartFile 类型的参数来接收前端上传的文件。 在接收到文件后,我们首先判断文件是否为空。如果不为空,我们通过 MultipartFile 类型的方法获取文件名和后缀名,并将文件存储到指定的路径下。最后,我们返回一个字符串,表示上传成功。 需要注意的是,在保存文件时,我们使用了 transferTo 方法。该方法会将上传文件保存到指定的路径下,并自动关闭文件流。同时,我们还判断了目录是否存在,如果不存在,就创建一个新的目录。 综上所述,通过以上的代码,我们可以实现 Spring Boot + Vue.js + ElementUI文件上传功能。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值