公司实现文件上传技术选型采用后端SpringBoot/Cloud,前端vue Bootstrap ,静态服务器作为文件存储,文件上传功能单独抽取封装文件上传组件,可供所有的文件的操作。
后端框架 | 版本 |
---|---|
SpringBoot | 2.5.6 |
Spring-Cloud | 2020.0.4 |
mysql | 8.0.26 |
pagehelper | 1.3.1 |
Mybatis | 2.2.0 |
Redis | 5.0 |
Fastjson | 1.2.78 |
前端框架 | 版本 |
---|---|
Vue | 2.6.11 |
axios | 0.24.0 |
vue-router | 3.5.3 |
Bootstrap | 4.6.2 |
文章目录
- 一、前端部分
- 1. 讲师页面
- 2. js部分
- 3. 文件上传组件
- 二、后端
- 2.1. 配置
- 2.2. 配置
- 2.3. api接口
一、前端部分
1. 讲师页面
讲师页面作为文件上传父页面
<div class="form-group"><label class="col-sm-2 control-label">头像</label><div class="col-sm-10"><file v-bind:input-id="'image-upload'"v-bind:text="'上传头像'"v-bind:suffixs="['jpg', 'jpeg', 'png']"v-bind:use="FILE_USE.TEACHER.key"v-bind:after-upload="afterUpload"></file><div v-show="teacher.image" class="row"><div class="col-md-4"><img v-bind:src="teacher.image" class="img-responsive"></div></div></div></div>
2. js部分
<script>
import File from "../../components/file";
export default {components: {Pagination, BigFile,File},name: "business-teacher",data: function () {return {teacher: {},teachers: [],FILE_USE: FILE_USE,}},methods: {/*** 点击【新增】*/add() {let _this = this;_this.teacher = {};$("#form-modal").modal("show");},/*** 点击【编辑】*/edit(teacher) {let _this = this;_this.teacher = $.extend({}, teacher);$("#form-modal").modal("show");},/*** 列表查询*/list(page) {let _this = this;Loading.show();_this.$api.post(process.env.VUE_APP_SERVER + '/business/admin/teacher/list', {page: page,size: _this.$refs.pagination.size,}).then((response) => {Loading.hide();let resp = response.data;_this.teachers = resp.content.list;_this.$refs.pagination.render(page, resp.content.total);})},/*** 点击【保存】*/save() {let _this = this;// 保存校验if (1 != 1|| !Validator.require(_this.teacher.name, "姓名")|| !Validator.length(_this.teacher.name, "姓名", 1, 50)|| !Validator.length(_this.teacher.nickname, "昵称", 1, 50)|| !Validator.length(_this.teacher.image, "头像", 1, 100)|| !Validator.length(_this.teacher.position, "职位", 1, 50)|| !Validator.length(_this.teacher.motto, "座右铭", 1, 50)|| !Validator.length(_this.teacher.intro, "简介", 1, 500)) {return;}Loading.show();_this.$api.post(process.env.VUE_APP_SERVER + '/business/admin/teacher/save', _this.teacher).then((response) => {Loading.hide();let resp = response.data;if (resp.success) {$("#form-modal").modal("hide");_this.list(1);Toast.success("保存成功!");} else {Toast.warning(resp.message)}})},afterUpload(resp) {let _this = thislet image = resp.content.path;_this.teacher.image = image}}
}
</script>
3. 文件上传组件
<template><div><button type="button" v-on:click="selectFile()" class="btn btn-white btn-default btn-round"><i class="ace-icon fa fa-upload"></i>{{text}}</button><input class="hidden" type="file" ref="file" v-on:change="uploadFile()" v-bind:id="inputId+'-input'"></div>a
</template><script>
export default {name: 'file',props: {text: {default: "上传文件"},inputId: {default: "file-upload"},suffixs: {default: []},use: {default: ""},afterUpload: {type: Function,default: null},},data: function () {return {}},methods: {uploadFile () {let _this = this;let formData = new window.FormData();let file = _this.$refs.file.files[0];// 判断文件格式let suffixs = _this.suffixs;let fileName = file.name;let suffix = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length).toLowerCase();let validateSuffix = false;for (let i = 0; i < suffixs.length; i++) {if (suffixs[i].toLowerCase() === suffix) {validateSuffix = true;break;}}if (!validateSuffix) {Toast.warning("文件格式不正确!只支持上传:" + suffixs.join(","));$("#" + _this.inputId + "-input").val("");return;}// key:"file"必须和后端controller参数名一致formData.append('file', file);formData.append('use', _this.use);Loading.show()_this.$api.post(process.env.VUE_APP_SERVER + '/file/admin/upload', formData).then((response) => {Loading.hide()let resp = response.dataconsole.log("上传文件成功:", resp)//回调父组件函数_this.afterUpload(resp)//解决 同一个文件上传2次或者大于3次,不会发生变化$("#" + _this.inputId + "-input").val("");})},selectFile() {let _this = this// console.log("_this.inputId",_this.inputId)$("#" + _this.inputId + "-input").trigger("click");}},
}
</script><style scoped>
</style>
二、后端
2.1. 配置
package com.course.file.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class SpingMvConfig implements WebMvcConfigurer {@Value("${file.path}")private String FILE_PATH;@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/f/**").addResourceLocations("file:" + FILE_PATH);}
}
2.2. 配置
# 应用名称
spring.application.name=file
# 应用端口
server.port=9003
# 注册到eureka
eureka.client.service-url.defaultZone=http://localhost:8761/eureka# 请求访问前缀
server.servlet.context-path=/file# 本地存储静态文件路径
file.path=D:/file/imooc/course/
# 访问静态文件路径(用于文件回显或者文件下载)
file.domain=http://127.0.0.1:9000/file/f/# 文件大小(如果搭建大小超过此配置的大小或抛出异常)
spring.servlet.multipart.max-file-size=50MB
# 请求大小
spring.servlet.multipart.max-request-size=50MB
2.3. api接口
package com.course.file.controller.admin;import java.util.Date;import com.course.server.dto.FileDto;
import com.course.server.dto.ResponseDto;
import com.course.server.enums.FileUseEnum;
import com.course.server.service.FileService;
import com.course.server.util.UuidUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;@RequestMapping("/admin")
@RestController
public class UploadController {public static final Logger LOG = LoggerFactory.getLogger(UploadController.class);public static final String BUSINESS_NAME = "文件上传";@Value("${file.domain}")private String FILE_DOMAIN;@Value("${file.path}")private String FILE_PATH;@Resourceprivate FileService fileService;@PostMapping("/upload")public ResponseDto upload(@RequestParam MultipartFile file, String use) throws IOException {String fileSize = String.valueOf(file.getSize());LOG.info("上传文件开始: {}", file);LOG.info("获取上传文件名称: {}", file.getOriginalFilename());LOG.info("上传文件大小: {}", fileSize);// 保存文件到本地FileUseEnum useEnum = FileUseEnum.getByCode(use);String key = UuidUtil.getShortUuid();String fileName = file.getOriginalFilename();String suffix = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();//如果文件夹不存在,则创建String dir = useEnum.name().toLowerCase();File fullDir = new File(FILE_PATH + dir);if (!fullDir.exists()) {fullDir.mkdirs();}String path = dir + File.separator + key + "." + suffix;String fullPath = FILE_PATH + path;File dest = new File(fullPath);file.transferTo(dest);LOG.info("上传文件全路径: {}", dest.getAbsolutePath());LOG.info("保存文件记录开始: {}");FileDto fileDto = new FileDto();fileDto.setPath(path);fileDto.setName(fileName);fileDto.setSuffix(suffix);fileDto.setUse(use);fileDto.setSize(Math.toIntExact(file.getSize()));fileService.save(fileDto);ResponseDto responseDto = new ResponseDto();fileDto.setPath(FILE_DOMAIN + path);responseDto.setContent(fileDto);return responseDto;}
}