• 【java开发技术积累篇】之springboot项目优美的文件上传方式


    一、首先抛出问题

    阿里云oss文件上传为例,假设我的需求是这样的,我需要发布一条动态,这条动态呢可以是图片、语音、视频,3种类型,每种类型的上传我必须要限制它的文件大小,超过了我就不能让他上传的。如果传统的方式,那就是创建3个上传类型bucket对应图片、语音和视频,其实这种做法是可以的,但是怎么说呢,还不够优雅,如果当这个动态有越来越多种类型,你是不是要建立N个类型对应呢,所以就会使得bucket特别的多,不好维护。

    二、解决思路

    首先,我们可以区分bucket的上传类型大类,比如动态是一个大类,它的子类呢有:图片、语音、视频,3种,每种的上传文件大小的限制是不一样的。文件上传后的位置统一都是放到大类bucketName文件夹上。我们可以建立一个枚举类,该枚举类比较特殊,是存储子分类数组的。code就是大分类,value是子分类数组。

    三、直接看源码就懂了,下面是controller

    1. @ApiOperation(value = "上传文件", notes = "bucketName选择[avatar(头像),file(文件),banner,game(游戏),dynamic(动态),room_bg_img(房间背景图)],文件上传按分类管理,其他类型请通知添加")
    2. @PostMapping("/uploadFile")
    3. public ResponseResult<UploadFileDto> uploadFile(@RequestParam MultipartFile file, @RequestParam String bucketName) {
    4. ResponseResult responseResult = ResponseResult.error();
    5. Entry entry = null;
    6. try {
    7. //限流
    8. entry = SphU.entry("uploadFile", EntryType.IN, 1, SessionUtil.getUserId());
    9. //验证是否是可上传的分类
    10. if (!BucketNameLimitSizeEnum.isValidEnum(bucketName)) {
    11. responseResult.setError(ResponseMessage.BUCKET_NAME_NOT_EXIST);
    12. return responseResult;
    13. }
    14. //获取文件限制大小
    15. FileLimitItem[] items = BucketNameLimitSizeEnum.getLimitItemByCode(bucketName);
    16. //判断文件属于什么类型
    17. Integer typeNum = FileTypeUtil.getContentType(file.getOriginalFilename());
    18. //判断该上传类型是哪种类型,并判断是否超过限制
    19. for (FileLimitItem item : items){
    20. if (typeNum == item.getType()){
    21. if (!MultipartFileUtil.checkFileSize(file.getSize(),item.getNum(),"K")){
    22. responseResult.setError(ResponseMessage.FILE_SIZE_BIG);
    23. return responseResult;
    24. }
    25. break;
    26. }
    27. }
    28. UploadFile entity = uploadFileService.upload(file, bucketName);
    29. UploadFileDto uploadFileDto = new UploadFileDto();
    30. BeanUtils.copyProperties(entity,uploadFileDto);
    31. responseResult = ResponseResult.success(uploadFileDto);
    32. }catch (BlockException e1) {
    33. /* 流控逻辑处理 - 开始 */
    34. log.warn("上传文件限流!");
    35. return ResponseResult.error(ResponseMessage.SENTINEL_ERROR);
    36. /* 流控逻辑处理 - 结束 */
    37. }catch (ApiException apiException){
    38. return ResponseResult.error(apiException.getResponseMessage());
    39. }catch (Exception e) {
    40. responseResult.setMessage(e.getMessage());
    41. log.error("上传文件异常",e);
    42. }finally {
    43. if (entry != null) {
    44. entry.exit();
    45. }
    46. }
    47. return responseResult;
    48. }
    49. 复制代码

    大分类枚举类

    1. /**
    2. * @Author huanxinLee
    3. * @Date 2021/10/18 20:25
    4. * @description : 单位 KB
    5. */
    6. public enum BucketNameLimitSizeEnum {
    7. AVATAR("avatar",new FileLimitItem[]{FileLimitItem.genImageItem(300)}), // 头像
    8. SCREENSHOTS("screenshots",new FileLimitItem[]{FileLimitItem.genImageItem(1536)}),//截屏,1.5M * 1024 = 1536KB
    9. ACTIVITY_IMG("activity_img",new FileLimitItem[]{FileLimitItem.genImageItem(1536)}),//活动类图片,1.5M * 1024 = 1536KB
    10. FILE("file",new FileLimitItem[]{FileLimitItem.genFileItem(20 * 1024)}), // 文件,20M
    11. BANNER("banner",new FileLimitItem[]{FileLimitItem.genImageItem(1536)}), // banner,1.5M
    12. GAME("game",new FileLimitItem[]{FileLimitItem.genFileItem(20 * 1024)}), // 游戏
    13. DYNAMIC("dynamic",new FileLimitItem[]{FileLimitItem.genImageItem(1536),FileLimitItem.genAudioItem(10 * 1024),FileLimitItem.genVideoItem(20 * 1024)}),//动态
    14. USER_REPORT("user_report",new FileLimitItem[]{FileLimitItem.genFileItem(20 * 1024)}), // 举报个人
    15. ;
    16. private String code;
    17. private FileLimitItem[] limitItems;
    18. BucketNameLimitSizeEnum(String code, FileLimitItem[] limitItems) {
    19. this.code = code;
    20. this.limitItems = limitItems;
    21. }
    22. public String getCode() {
    23. return code;
    24. }
    25. public void setCode(String code) {
    26. this.code = code;
    27. }
    28. public FileLimitItem[] getLimitItems() {
    29. return limitItems;
    30. }
    31. public void setLimitItems(FileLimitItem[] limitItems) {
    32. this.limitItems = limitItems;
    33. }
    34. //通过code获取限制大小数组
    35. public static FileLimitItem[] getLimitItemByCode(String code){
    36. for (BucketNameLimitSizeEnum bucketNameLimitSizeEnum : BucketNameLimitSizeEnum.values()){
    37. if (bucketNameLimitSizeEnum.getCode().equals(code)) {
    38. return bucketNameLimitSizeEnum.getLimitItems();
    39. }
    40. }
    41. return null;
    42. }
    43. //判断是否存在该上传类
    44. public static boolean isValidEnum(String code) {
    45. for (BucketNameLimitSizeEnum typeEnum : BucketNameLimitSizeEnum.values()) {
    46. if (typeEnum.getCode().equals(code)) {
    47. return true;
    48. }
    49. }
    50. return false;
    51. }
    52. }
    53. 复制代码

    枚举子分类数组,主要把所有上传的类型分为4大类:图片、音频、视频、文件

    1. /**
    2. * @Author huanxinLee
    3. * @Date 2021/10/18 21:08
    4. * @description : 文件上传 分类
    5. */
    6. public class FileLimitItem {
    7. public static final int TYPE_IMAGE = 1;//图片
    8. public static final int TYPE_AUDIO = 2;//音频
    9. public static final int TYPE_VIDEO = 3;//视频
    10. public static final int TYPE_FILE = 4;//文件
    11. private int type;
    12. private int num;
    13. FileLimitItem() {
    14. }
    15. FileLimitItem(int type, int num) {
    16. this.type = type;
    17. this.num = num;
    18. }
    19. // image
    20. public static FileLimitItem genImageItem(int num){
    21. return new FileLimitItem(TYPE_IMAGE,num);
    22. }
    23. // audio
    24. public static FileLimitItem genAudioItem(int num){
    25. return new FileLimitItem(TYPE_AUDIO,num);
    26. }
    27. // video
    28. public static FileLimitItem genVideoItem(int num){
    29. return new FileLimitItem(TYPE_VIDEO,num);
    30. }
    31. // file
    32. public static FileLimitItem genFileItem(int num){
    33. return new FileLimitItem(TYPE_FILE,num);
    34. }
    35. public int getType() {
    36. return type;
    37. }
    38. public void setType(int type) {
    39. this.type = type;
    40. }
    41. public int getNum() {
    42. return num;
    43. }
    44. public void setNum(int num) {
    45. this.num = num;
    46. }
    47. }
    48. 复制代码

    判断文件大小是否超的工具类

    1. /**
    2. * @Author huanxinLee
    3. * @Date 2021/10/18 20:44
    4. * @description : 文件上传限制大小工具类
    5. */
    6. public class MultipartFileUtil {
    7. /**
    8. * @param len 文件长度
    9. * @param size 限制大小
    10. * @param unit 限制单位(B,K,M,G)
    11. * @描述 判断文件大小
    12. */
    13. public static boolean checkFileSize(Long len, int size, String unit) {
    14. double fileSize = 0;
    15. if ("B".equalsIgnoreCase(unit)) {
    16. fileSize = (double) len;
    17. } else if ("K".equalsIgnoreCase(unit)) {
    18. fileSize = (double) len / 1024;
    19. } else if ("M".equalsIgnoreCase(unit)) {
    20. fileSize = (double) len / 1048576;
    21. } else if ("G".equalsIgnoreCase(unit)) {
    22. fileSize = (double) len / 1073741824;
    23. }
    24. return !(fileSize > size);
    25. }
    26. /**
    27. * file转multipartFile
    28. */
    29. public static MultipartFile fileToMultipartFile(File file) {
    30. FileItemFactory factory = new DiskFileItemFactory(16, null);
    31. FileItem item=factory.createItem(file.getName(),"text/plain",true,file.getName());
    32. int bytesRead = 0;
    33. byte[] buffer = new byte[8192];
    34. try {
    35. FileInputStream fis = new FileInputStream(file);
    36. OutputStream os = item.getOutputStream();
    37. while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
    38. os.write(buffer, 0, bytesRead);
    39. }
    40. os.close();
    41. fis.close();
    42. } catch (IOException e) {
    43. e.printStackTrace();
    44. }
    45. return new CommonsMultipartFile(item);
    46. }
    47. /**
    48. * inputStream 转 File
    49. */
    50. public static File inputStreamToFile(InputStream ins, String name) throws Exception{
    51. //System.getProperty("java.io.tmpdir")临时目录+File.separator目录中间的间隔符+文件名
    52. File file = new File(System.getProperty("java.io.tmpdir") + File.separator + name);
    53. // if (file.exists()) {
    54. // return file;
    55. // }
    56. OutputStream os = new FileOutputStream(file);
    57. int bytesRead;
    58. int len = 8192;
    59. byte[] buffer = new byte[len];
    60. while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
    61. os.write(buffer, 0, bytesRead);
    62. }
    63. os.close();
    64. ins.close();
    65. return file;
    66. }
    67. }
    68. 复制代码

    四、结尾

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


    结语:以往都是看别人的博客进行学习技术,其中不乏有精华博客也有吊儿郎当的CV大法文章,所以决定将自己所学所用所整理的知识分享给大家,主要还是想为了后浪们少走些弯路,多些正能量的博客,如有错漏,欢迎指正,仅希望大家能在我的博客中学到知识,解决到问题,那么就足够了。谢谢大家!(转载请注明原文出处)

  • 相关阅读:
    Java中VO、DO、PO、DTO之间的模型转换
    一次搞懂什么是大数据
    esxi 6.7下安装黑裙
    折纸问题
    ZZULIOJ:1158: 又是排序(指针专题)
    我要给你讲的简单明了,Java就是值传递,不服来辩
    Go语言并发编程——原子操作
    自定义异常、实际应用中的经验总结
    React中的Virtual DOM(看这一篇就够了)
    Java8常用日期时间方法
  • 原文地址:https://blog.csdn.net/BASK2311/article/details/127994907