Spring 引导 1.3.x MultipartFile.transferTo 从 1.2.x 迁移后为空

Spring boot 1.3.x MultipartFile.transferTo null after migration from 1.2.x

从 Spring Boot 1.2.7 升级到 1.3.1 后一天,我遇到了 MultipartFile 错误。

我注意到现在默认是 Jetty 9.2,不再是 Tomcat8。一切都很好,直到我尝试使用 MultipartFile.transferTo(File file) 方法写入上传的文件..

MultipartFile.transferTo() 方法正在调用 javax.servlet.http.Part 的实现,它以这种方式实现 tomcat 8

@Override
public void write(String fileName) throws IOException {
    File file = new File(fileName);
    if (!file.isAbsolute()) {
        file = new File(location, fileName);
    }
    try {
        fileItem.write(file);
    } catch (Exception e) {
        throw new IOException(e);
    }
}

码头 9.2 的这种方式

    public void write(String fileName) throws IOException
    {
        if (_file == null)
        {
            _temporary = false;

            //part data is only in the ByteArrayOutputStream and never been written to disk
            _file = new File (_tmpDir, fileName);

            BufferedOutputStream bos = null;
            try
            {
                bos = new BufferedOutputStream(new FileOutputStream(_file));
                _bout.writeTo(bos);
                bos.flush();
            }
            finally
            {
                if (bos != null)
                    bos.close();
                _bout = null;
            }
        }
        else
        {
            //the part data is already written to a temporary file, just rename it
            _temporary = false;

            File f = new File(_tmpDir, fileName);
            if (_file.renameTo(f))
                _file = f;
        }
    }

Jetty 实现的错误在于等待文件名 File.getName() 而不是 StandardMultipartHttpServletRequest.transferTo(File file)[=23= 的调用提供的绝对路径名 File.getPath() ]

    @Override
    public void transferTo(File dest) throws IOException, IllegalStateException {
        this.part.write(dest.getPath());
    }

这是一个错误吗?请注意,这是因为我已从 spring boot 1.2.7 升级到 1.3.1。默认是 Tomcat,现在是 Jetty...

好的,目前如果你想摆脱这个错误,你可以切换回 Tomcat 而不是 Jetty。

将 tomcat 放入您的依赖项中:

compile('org.springframework.boot:spring-boot-starter-tomcat')

并声明 tomcat 为容器:

@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
    return new TomcatEmbeddedServletContainerFactory();
}

根据 javax.servlet.http.Part.write(String filename) 的 javadoc,filename 参数是 ...

The file is created relative to the location as specified in the MultipartConfig

在您在 Jetty 9.2 中引用的代码中,即这个...

jetty-9.2.14.v20151106 - MultiPartInputStreamParser.write(String fileName)

您会看到它有 2 种可能的代码路径,第一个是 "in memory" 路径,第二个是 "file on disk" 方法。

在这两种情况下,当您为 Part.write(String) 指定一个文件名时,该名称是相对于您的 MultiPartConfig.location(您在问题中没有详细说明的配置)。

MultiPartInputStreamParser 的实现有一个 _tmpDir which is configured from the webapp's MultiPartConfig.location

如果您希望它正常运行,强烈建议您定义一个适合您的应用程序的 MultiPartConfig.location,而不是依赖容器来选择一个。

允许在 Part.write(String) 中使用绝对文件名的 Tomcat 方法在 servlet 规范中实际上是不允许的(主要是因为它是一个安全问题,可用于对系统造成严重破坏)