从前端到后端全面解析文件上传
- 1.前端准备(vue+element-ui)
- 2.后端准备(SpringBoot+minio+mysql)
- 2.1解决跨域
- 2.2配置minio与mysql
- 2.3controller层
- 2.4service层
1.前端准备(vue+element-ui)
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>index</title><link rel="stylesheet" href="./element-ui/lib/theme-chalk/index.css"><link rel="stylesheet" href="css/index.css"><script src="js/vue.js"></script><script src="js/axios-0.18.0.js"></script><script src="./element-ui/lib/index.js"></script><style>.avatar-uploader .el-upload {border: 1px dashed #d9d9d9;border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;}.avatar-uploader .el-upload:hover {border-color: #409EFF;}.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;line-height: 178px;text-align: center;}.avatar {width: 178px;height: 178px;display: block;}</style>
</head>
<style>
</style>
<body><div id="app"><el-uploadclass="avatar-uploader"action="http://localhost:8088/members/upload":show-file-list="false":on-success="handleAvatarSuccess":before-upload="beforeAvatarUpload"><img v-if="imageUrl" :src="imageUrl" class="avatar"><i v-else class="el-icon-plus avatar-uploader-icon"></i><p>{{name}}</p></el-upload>
</div><script>new Vue({el: "#app",data() {return {imageUrl: '',name:'',url:'',};},methods: {handleAvatarSuccess(res, file) {this.imageUrl = URL.createObjectURL(file.raw);this.name=file.response.namethis.url=file.response.urlconsole.log(file)},beforeAvatarUpload(file) {const isLt2M = file.size / 1024 / 1024 < 10;if (!isLt2M) {this.$message.error('上传头像图片大小不能超过 10MB!');}return isLt2M;}}})
</script>
</body>
</html>
2.后端准备(SpringBoot+minio+mysql)
2.1解决跨域
package com.data211.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;/*** @author rjj* @date 2023/2/3 - 15:15*/
@Configuration
public class GlobalCorsConfig {/*** 允许跨域调用的过滤器*/@Beanpublic CorsFilter corsFilter() {CorsConfiguration config = new CorsConfiguration();//允许白名单域名进行跨域调用(设置http://localhost:8080/ 表示指定请求源允许跨域)config.addAllowedOriginPattern("*");//允许跨越发送cookieconfig.setAllowCredentials(true);//放行全部原始头信息config.addAllowedHeader("*");//允许所有请求方法跨域调用config.addAllowedMethod("*");UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();//指定拦截路径source.registerCorsConfiguration("/**", config);return new CorsFilter(source);}
}
2.2配置minio与mysql
pom依赖
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency><!--第三方工具jar包--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.17</version></dependency>
配置文件配置
server:port: 8088spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: username: rootpassword: mybatis-plus:mapper-locations: classpath:mapper/*.xml
# 配置别名type-aliases-package: com.data211.pojoglobal-config:db-config:
# 主键自增长id-type: auto
# 表名前缀table-prefix: data211_
# 逻辑删除logic-delete-value: 1logic-not-delete-value: 0
# 控制台输出操作数据库日志configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplminio:endpoint: accessKey: secretKey: bucket:files:
配置minio客户端
package com.data211.config;import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/**@author rjj@date 2023/2/18 - 17:37
*/
@Configuration
public class MinioConfig {@Value("${minio.endpoint}")private String endpoint;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;@Beanpublic MinioClient minioClient() {MinioClient minioClient =MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();return minioClient;}
}
2.3controller层
@RequestMapping(value = "/upload")public UploadFileResultDto upload(@RequestPart("file") MultipartFile file) throws IOException {return membersService.upload(file);};
2.4service层
package com.data211.service.impl;import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.data211.dao.MembersDao;
import com.data211.dto.UploadFileResultDto;
import com.data211.pojo.MediaFiles;
import com.data211.pojo.Members;
import com.data211.service.IMembersService;
import com.data211.utils.BaseContext;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;@Service
public class MembersServiceImpl extends ServiceImpl<MembersDao, Members> implements IMembersService {@Resourceprivate MinioClient minioClient;//普通文件桶@Value("${minio.bucket.files}")private String bucket_Files;@Resourceprivate MembersServiceImpl membersService;@Resourceprivate MediaFilesServiceImpl mediaFilesService;@Overridepublic UploadFileResultDto upload(MultipartFile file) throws IOException {String fileMd5 = DigestUtil.md5Hex(file.getBytes());String folder = getFileFolder(new Date(), true, true, true);String filename = fileMd5+file.getName().substring(file.getName().lastIndexOf("."));MediaFiles mediaFiles = null;try {//TODO 上传到minioaddMediaFilesToMinIO(file, bucket_Files, folder+filename);//TODO 上传到数据库 (用Spring控制的代理对象实现事务控制生效)mediaFiles = membersService.addMediaFilesToDb(BaseContext.getUserId(),filename,folder+filename);UploadFileResultDto uploadFileParamsDto = new UploadFileResultDto();BeanUtils.copyProperties(mediaFiles,uploadFileParamsDto);return uploadFileParamsDto;} catch (Exception e) {e.printStackTrace();}return null;}@Override@Transactionalpublic MediaFiles addMediaFilesToDb(String userId, String filename,String url) {MediaFiles mediaFiles = new MediaFiles(userId, filename, url);mediaFilesService.save(mediaFiles);return mediaFiles;}public void addMediaFilesToMinIO(MultipartFile file, String bucket, String objectName) throws IOException {// 将文件字节输入到内存流中ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(file.getBytes());//获取文件类型String contentType = file.getContentType();try {PutObjectArgs putObjectArgs =PutObjectArgs.builder().bucket(bucket).object(objectName)//-1 表示文件分片按 5M(不小于 5M,不大于 5T),分片数量最大10000.stream(byteArrayInputStream, byteArrayInputStream.available(), -1).contentType(contentType).build();minioClient.putObject(putObjectArgs);} catch (Exception e) {e.printStackTrace();}}private String getFileFolder(Date date, boolean year, boolean month, boolean day) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");//获取当前日期字符串String dateString = sdf.format(new Date());//取出年、月、日String[] dateStringArray = dateString.split("-");StringBuffer folderString = new StringBuffer();if (year) {folderString.append(dateStringArray[0]);folderString.append("/");}if (month) {folderString.append(dateStringArray[1]);folderString.append("/");}if (day) {folderString.append(dateStringArray[2]);folderString.append("/");}return folderString.toString();}}