Search code examples
spring-bootembedded-jettyjetty-9

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


I am facing error MultipartFile error for one day after having upgrade from Spring Boot 1.2.7 to 1.3.1.

What I notice is that the default is now Jetty 9.2 and no more Tomcat 8. Everything was fine until I tried to write an uploaded file using MultipartFile.transferTo(File file) method..

MultipartFile.transferTo() method is calling an implementation of javax.servlet.http.Part Which is implemented this way for 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);
    }
}

and this way for jetty 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;
        }
    }

What's wrong with the Jetty implementation is that is waiting for file name File.getName() and not the absolute path name File.getPath() which is provided by the call of StandardMultipartHttpServletRequest.transferTo(File file)

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

Is this a bug ? Note that this occurs since I have upgraded from spring boot 1.2.7 to 1.3.1. The default was Tomcat and now it is Jetty...


Solution

  • Per the javadoc for javax.servlet.http.Part.write(String filename) the filename parameter is ...

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

    In the code you referenced in Jetty 9.2, namely this ...

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

    You'll see that there's 2 possible code paths it takes, the first is the "in memory" path, and the second is "file on disk" approach.

    In both cases, when you specify a filename to Part.write(String) that name is relative to your MultiPartConfig.location (a configuration of which you haven't detailed in your question).

    The implementation of MultiPartInputStreamParser has a _tmpDir which is configured from the webapp's MultiPartConfig.location.

    If you want this to behave properly, would highly recommend you define a MultiPartConfig.location that is appropriate for your application, instead of relying on the container to pick one.

    The Tomcat approach of allowing absolute filenames in Part.write(String) is actually not allowed in the servlet spec (mainly as its a security issue that can be used to cause havoc on a system)