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"])

分析:客户端接收到的是 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()...
     // ...
 }

2)byte[] 转 Base64 字符串

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

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

String bytesStr = Base64.encodeBase64String(bytes);

3)Base64 字符串 转 byte[]

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

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

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;
    }
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐