SpringBoot实现项目文件上传的方法详解

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

SpringBoot实现项目文件上传的方法详解

狐狸与小兔的故事   2022-11-24 我要评论

一、首先抛出问题

以阿里云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;
    }
}

四、结尾

优雅的文件上传其实只要认真看了我上面的代码,就懂得了。主要是大枚举内存的是数组类,这种写法比较少见,但是却能解决多种不同类型文字限制大小的代码拓展性。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们