【java开发技术积累篇】之springboot项目优美的文件上传方式
文件上传目前主流的就是阿里云的oss或MINIO了,这些大家都懂,但是如何写一个优雅的拓展性强的上传方式呢?在此将自己的经验描述一下,让各位后辈能学习到东西。。。
一、首先抛出问题
以阿里云oss文件上传为例,假设我的需求是这样的,我需要发布一条动态,这条动态呢可以是图片、语音、视频,3种类型,每种类型的上传我必须要限制它的文件大小,超过了我就不能让他上传的。如果传统的方式,那就是创建3个上传类型bucket对应图片、语音和视频,其实这种做法是可以的,但是怎么说呢,还不够优雅,如果当这个动态有越来越多种类型,你是不是要建立N个类型对应呢,所以就会使得bucket特别的多,不好维护。
二、解决思路
首先,我们可以区分bucket的上传类型大类,比如动态是一个大类,它的子类呢有:图片、语音、视频,3种,每种的上传文件大小的限制是不一样的。文件上传后的位置统一都是放到大类bucketName文件夹上。我们可以建立一个枚举类,该枚举类比较特殊,是存储子分类数组的。code就是大分类,value是子分类数组。
三、直接看源码就懂了,下面是controller
@ApiOperation(value = "上传文件", notes = "bucketName选择[avatar(头像),file(文件),banner,game(游戏),dynamic(动态),room_bg_img(房间背景图)],文件上传按分类管理,其他类型请通知添加")
@PostMapping("/uploadFile")
public ResponseResult<UploadFileDto> uploadFile(@RequestParam MultipartFile file, @RequestParam String bucketName) {
ResponseResult responseResult = ResponseResult.error();
Entry entry = null;
try {
//限流
entry = SphU.entry("uploadFile", EntryType.IN, 1, SessionUtil.getUserId());
//验证是否是可上传的分类
if (!BucketNameLimitSizeEnum.isValidEnum(bucketName)) {
responseResult.setError(ResponseMessage.BUCKET_NAME_NOT_EXIST);
return responseResult;
}
//获取文件限制大小
FileLimitItem[] items = BucketNameLimitSizeEnum.getLimitItemByCode(bucketName);
//判断文件属于什么类型
Integer typeNum = FileTypeUtil.getContentType(file.getOriginalFilename());
//判断该上传类型是哪种类型,并判断是否超过限制
for (FileLimitItem item : items){
if (typeNum == item.getType()){
if (!MultipartFileUtil.checkFileSize(file.getSize(),item.getNum(),"K")){
responseResult.setError(ResponseMessage.FILE_SIZE_BIG);
return responseResult;
}
break;
}
}
UploadFile entity = uploadFileService.upload(file, bucketName);
UploadFileDto uploadFileDto = new UploadFileDto();
BeanUtils.copyProperties(entity,uploadFileDto);
responseResult = ResponseResult.success(uploadFileDto);
}catch (BlockException e1) {
/* 流控逻辑处理 - 开始 */
log.warn("上传文件限流!");
return ResponseResult.error(ResponseMessage.SENTINEL_ERROR);
/* 流控逻辑处理 - 结束 */
}catch (ApiException apiException){
return ResponseResult.error(apiException.getResponseMessage());
}catch (Exception e) {
responseResult.setMessage(e.getMessage());
log.error("上传文件异常",e);
}finally {
if (entry != null) {
entry.exit();
}
}
return responseResult;
}
大分类枚举类
/**
* @Author huanxinLee
* @Date 2021/10/18 20:25
* @description : 单位 KB
*/
public enum BucketNameLimitSizeEnum {
AVATAR("avatar",new FileLimitItem[]{FileLimitItem.genImageItem(300)}), // 头像
SCREENSHOTS("screenshots",new FileLimitItem[]{FileLimitItem.genImageItem(1536)}),//截屏,1.5M * 1024 = 1536KB
ACTIVITY_IMG("activity_img",new FileLimitItem[]{FileLimitItem.genImageItem(1536)}),//活动类图片,1.5M * 1024 = 1536KB
FILE("file",new FileLimitItem[]{FileLimitItem.genFileItem(20 * 1024)}), // 文件,20M
BANNER("banner",new FileLimitItem[]{FileLimitItem.genImageItem(1536)}), // banner,1.5M
GAME("game",new FileLimitItem[]{FileLimitItem.genFileItem(20 * 1024)}), // 游戏
DYNAMIC("dynamic",new FileLimitItem[]{FileLimitItem.genImageItem(1536),FileLimitItem.genAudioItem(10 * 1024),FileLimitItem.genVideoItem(20 * 1024)}),//动态
USER_REPORT("user_report",new FileLimitItem[]{FileLimitItem.genFileItem(20 * 1024)}), // 举报个人
;
private String code;
private FileLimitItem[] limitItems;
BucketNameLimitSizeEnum(String code, FileLimitItem[] limitItems) {
this.code = code;
this.limitItems = limitItems;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public FileLimitItem[] getLimitItems() {
return limitItems;
}
public void setLimitItems(FileLimitItem[] limitItems) {
this.limitItems = limitItems;
}
//通过code获取限制大小数组
public static FileLimitItem[] getLimitItemByCode(String code){
for (BucketNameLimitSizeEnum bucketNameLimitSizeEnum : BucketNameLimitSizeEnum.values()){
if (bucketNameLimitSizeEnum.getCode().equals(code)) {
return bucketNameLimitSizeEnum.getLimitItems();
}
}
return null;
}
//判断是否存在该上传类
public static boolean isValidEnum(String code) {
for (BucketNameLimitSizeEnum typeEnum : BucketNameLimitSizeEnum.values()) {
if (typeEnum.getCode().equals(code)) {
return true;
}
}
return false;
}
}
枚举子分类数组,主要把所有上传的类型分为4大类:图片、音频、视频、文件
/**
* @Author huanxinLee
* @Date 2021/10/18 21:08
* @description : 文件上传 分类
*/
public class FileLimitItem {
public static final int TYPE_IMAGE = 1;//图片
public static final int TYPE_AUDIO = 2;//音频
public static final int TYPE_VIDEO = 3;//视频
public static final int TYPE_FILE = 4;//文件
private int type;
private int num;
FileLimitItem() {
}
FileLimitItem(int type, int num) {
this.type = type;
this.num = num;
}
// image
public static FileLimitItem genImageItem(int num){
return new FileLimitItem(TYPE_IMAGE,num);
}
// audio
public static FileLimitItem genAudioItem(int num){
return new FileLimitItem(TYPE_AUDIO,num);
}
// video
public static FileLimitItem genVideoItem(int num){
return new FileLimitItem(TYPE_VIDEO,num);
}
// file
public static FileLimitItem genFileItem(int num){
return new FileLimitItem(TYPE_FILE,num);
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
判断文件大小是否超的工具类
/**
* @Author huanxinLee
* @Date 2021/10/18 20:44
* @description : 文件上传限制大小工具类
*/
public class MultipartFileUtil {
/**
* @param len 文件长度
* @param size 限制大小
* @param unit 限制单位(B,K,M,G)
* @描述 判断文件大小
*/
public static boolean checkFileSize(Long len, int size, String unit) {
double fileSize = 0;
if ("B".equalsIgnoreCase(unit)) {
fileSize = (double) len;
} else if ("K".equalsIgnoreCase(unit)) {
fileSize = (double) len / 1024;
} else if ("M".equalsIgnoreCase(unit)) {
fileSize = (double) len / 1048576;
} else if ("G".equalsIgnoreCase(unit)) {
fileSize = (double) len / 1073741824;
}
return !(fileSize > size);
}
/**
* file转multipartFile
*/
public static MultipartFile fileToMultipartFile(File file) {
FileItemFactory factory = new DiskFileItemFactory(16, null);
FileItem item=factory.createItem(file.getName(),"text/plain",true,file.getName());
int bytesRead = 0;
byte[] buffer = new byte[8192];
try {
FileInputStream fis = new FileInputStream(file);
OutputStream os = item.getOutputStream();
while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return new CommonsMultipartFile(item);
}
/**
* inputStream 转 File
*/
public static File inputStreamToFile(InputStream ins, String name) throws Exception{
//System.getProperty("java.io.tmpdir")临时目录+File.separator目录中间的间隔符+文件名
File file = new File(System.getProperty("java.io.tmpdir") + File.separator + name);
// if (file.exists()) {
// return file;
// }
OutputStream os = new FileOutputStream(file);
int bytesRead;
int len = 8192;
byte[] buffer = new byte[len];
while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.close();
ins.close();
return file;
}
}
四、结尾
优雅的文件上传其实只要认真看了我上面的代码,就懂得了。主要是大枚举内存的是数组类,这种写法比较少见,但是却能解决多种不同类型文字限制大小的代码拓展性。
结语:以往都是看别人的博客进行学习技术,其中不乏有精华博客也有吊儿郎当的CV大法文章,所以决定将自己所学所用所整理的知识分享给大家,主要还是想为了后浪们少走些弯路,多些正能量的博客,如有错漏,欢迎指正,仅希望大家能在我的博客中学到知识,解决到问题,那么就足够了。谢谢大家!(转载请注明原文出处)
转载自:https://juejin.cn/post/7168762309039849509