Fastjson: 文件上传请求参数使用fastjson序列化后文件资源被清空

Created on 17 Nov 2020  ·  5Comments  ·  Source: alibaba/fastjson

我在后端项目里使用RestTemplate请求接口上传文件,有个RestTemplate的切面会打印请求参数,调用JSON.toJSONString()方法对参数序列化后,本地文件被清空,文件大小为0,上传到文件服务器的文件大小也是0;

fastjson版本:1.2.69

代码逻辑:
Map map = new HashMap<>();
map.put("file", new FileSystemResource(new File("filePath")));
System.out.println(JSON.toJSONString(map));

麻烦大佬帮忙解答一下

All 5 comments

FileSystemResource 是spring封装的类,其实现了WritableResource接口

public class FileSystemResource extends AbstractResource implements WritableResource {...}

在递归解析字段时,由于是采用getter方式,getOutputStream被当做getter方法被调用,

public interface WritableResource extends Resource {
        // omit

    /**
     * Return an {@link OutputStream} for the underlying resource,
     * allowing to (over-)write its content.
     * @throws IOException if the stream could not be opened
     * @see #getInputStream()
     */
    OutputStream getOutputStream() throws IOException;
}

getXX本意是获取XX字段的值,但getOutputStream()被调用,拿到output后未写入任何值,相当于向output写入了空,后被flush到磁盘导致的文件被重写了

解决办法

  1. 不要toJsonString打印复杂对象,特别是不熟悉的class
  2. 避免使用FileSystemResource类,使用其他对象代替

这个不是拿到outputStream 后写入了空,导致了文件被重写,而是在构造FileOutputStream 对象的时候就已经清空了文件,
具体应该是在这里

public FileOutputStream(File file, boolean append)
        throws FileNotFoundException
    {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkWrite(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        this.fd = new FileDescriptor();
        fd.attach(this);
        this.append = append;
        this.path = name;

        open(name, append);
    }


// 这里文件已经被清空了
   /**
     * Opens a file, with the specified name, for overwriting or appending.
     * @param name name of file to be opened
     * @param append whether the file is to be opened in append mode
     */
    private native void open0(String name, boolean append)
        throws FileNotFoundException;

    // wrap native call to allow instrumentation
    /**
     * Opens a file, with the specified name, for overwriting or appending.
     * @param name name of file to be opened
     * @param append whether the file is to be opened in append mode
     */
    private void open(String name, boolean append)
        throws FileNotFoundException {
        open0(name, append);
    }

也就是说,如果构造FileOutputStrem 时,append=false(缺省值) 会先清空文件

查了一下c 语言的文档,文件打开模式为w+ 时,会截断

模式 | 描述
-- | --
r | 打开一个已有的文本文件,允许读取文件。
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+ | 打开一个文本文件,允许读写文件。
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。

i can't agree with you more

@hulog @ChenYuPan 多谢两位大佬解疑答惑~

Was this page helpful?
0 / 5 - 0 ratings