• SpringCloud 下 MultipartFile 序列化(JSON)出错的解决方案


    1、需求

    在SpringCloud架构下,用户向客户端上传文件,客户端调用文件处理微服务去处理文件

    2、问题

    客户端文件处理服务间传递文件时,想直接把 MultipartFile 转为 json,但出现异常。

    在这里插入图片描述

    Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class java.io.FileDescriptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile["inputStream"]->java.io.FileInputStream["fd"])] with root cause
    
    com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile["inputStream"]->java.io.FileInputStream["fd"])
    
    • 1
    • 2
    • 3

    分析:客户端接收到的是 MultipartFile 文件;要把文件传给文件处理微服务,但直接把 MultipartFile 传给文件服务是不行的。 MultipartFile 不能序列化(或者比较麻烦);

    有人说在实体类上加注解,忽略掉MultipartFile属性;@JSONField(serialize = false),但是我的实体类没有 MultipartFile 属性。

    经过一番查找,找到了一个合适的解决方案。

    3、解决方案

    3.1、原理图

    在这里插入图片描述

    • 客户端接收到 MultipartFile 文件

    • 通过 MultipartFilegetBytes() 方法转为 byte[] 数组

    • 通过 Base64 工具类把 byte[] 数组转为 Base64字符串,并发送给 文件处理服务

    • 文件处理服务收到 Base64字符串时,再用 Base64 工具类把 字符串转为 byte[] 数组

    • byte[] 作为输入流,从中读取数据,写入输出流,完成文件的存储

    3.2、实现步骤

    1)MultipartFile 转 byte[]

    客户端接收到客户上传的MultipartFile文件

     @PostMapping("/add")
     public String add(
         @RequestParam("file") MultipartFile file
     ) {
         // ...
         byte[] bytes = file.getBytes();
         // 同时获取文件类型;实现步骤省略...
         String fileType = file.getOriginalFilename()...
         // ...
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2)byte[] 转 Base64 字符串

    工具类 org.apache.tomcat.util.codec.binary.Base64

    byte[] 转为字符串后,可以发送给 文件处理服务,同时把文件类型也一起发送;因为从 byte[] 中解析文件类型比较麻烦

    String bytesStr = Base64.encodeBase64String(bytes);
    
    • 1

    3)Base64 字符串 转 byte[]

    文件处理服务 收到 字符串后,再转为 byte[]

    byte[] bytes = Base64.decodeBase64(bytesStr);
    
    • 1

    4)从 byte[] 中读取文件并保存

        /**
         * 保存 byte[] 形式的文件
         * @param bytesOfFile 以 byte[] 形式存储的文件
         * @param fileType 文件类型
         * @param saveDir 存储目录
         * @return
         */
        public static String saveFileByBytes(
                byte[] bytesOfFile,
                String fileType,
                String saveDir){
    
            // 输入流
            BufferedInputStream bis = null;
            // 输出流
            BufferedOutputStream bos = null;
    
            String fileName = "";
            try {
                // 输入流
                bis = new BufferedInputStream(new ByteArrayInputStream(bytesOfFile));
                // 输出流
                // --- 新文件(名 + 类型);雪花算法省略...
                fileName = SnowflakeUtil.getInstance().nextId() + "." + fileType;
                bos = new BufferedOutputStream(new FileOutputStream(new File(saveDir, fileName)));
                int readLen;
                byte[] buf = new byte[8192];
                while ((readLen = bis.read(buf)) > -1) {
                    bos.write(buf, 0, readLen);
                }
            } catch (IOException e) {
                e.printStackTrace();
                log.info("文件存储异常!");
                return "_err";
            } finally {
                // 关闭
                try {
                    if (bis != null) {
                        bis.close();
                    }
                    if (bos != null) {
                        bos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 返回保存后的文件名
            return fileName;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
  • 相关阅读:
    2022英伟达显卡排名天梯图
    【软件分析课程11讲-学习笔记】布尔可满足性SAT
    Linux—文件编程
    cloudenative2-1-go进阶
    前端面试宝典React篇09 Virtual DOM 的工作原理是什么?
    HDU - 1078 FatMouse and Cheese(记忆化搜索DP)
    Scala爬虫如何实时采集天气数据?
    什么是平面设计呢?优漫教育
    LeetCode530.二叉搜索树的最小绝对差 501二叉搜索树中的众数 236二叉树的最近公共祖先
    安卓开机启动流程
  • 原文地址:https://blog.csdn.net/tu_wer/article/details/126602095