Vue Bootstrap 静态服务器 实现文件上传
公司实现文件上传技术选型采用后端SpringBoot/Cloud,前端vue Bootstrap,阿里云OSS作为文件存储,文件上传功能单独抽取封装文件上传组件,可供所有的文件的操作。后端框架版本SpringBoot2.5.6Spring-Cloud2020.0.4mysql8.0.26pagehelper1.3.1Mybatis2.2.0Redis5.0Fastjson1.2.78前端框架版本.
·
公司实现文件上传技术选型采用后端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. 讲师页面
讲师页面作为文件上传父页面
<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 = this
let 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.data
console.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;
@Override
public 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;
@Resource
private 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;
}
}
更多推荐
已为社区贡献25条内容
所有评论(0)