[alibaba/fastjson]文件上传请求参数使用fastjson序列化后文件资源被清空

2024-09-20 632 views
9

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

fastjson版本:1.2.69

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

麻烦大佬帮忙解答一下

回答

8

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类,使用其他对象代替
0

这个不是拿到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(缺省值) 会先清空文件

3

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

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

i can't agree with you more

2

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

3

很赞

4

首先我们得知道fastJson是基于getter和setter,并不是基于field的。 我们观察输出和类结构就可以发现: image a15c8c56838986ab0279412be76fce4 JSON.toJSONString(map)这段代码将所有get方法调用了一遍以获取信息。 所以,FileSystemResource.getOutputStream() 被执行了。 在断点测试中,我发现此方法执行完,文件变为0kb。 我们看看这个方法干了什么: image image 然后点击newOutputStream方法文档 2532765bf290df35d8ab4613347a520 标红的地方:换句话来说,如果文件不存在则创建文件,如果文件存在则将现有文件截断为0KB。 总结:首先fastJson调用了非属性get方法:getOutputStream(),导致调用 newOutputStream()使文件被截断。 所以说今后需要注意的地方是:类里面如果有奇奇怪怪的不是属性的get方法,不要用JSON.toJSONString。