Steps to Reproduce
When spring boot application starts with default value of server.tomcat.basedir
in application.properties
. It creates two folders in /tmp
folder. /tmp/tomcat.xxxxx/..
and /tmp-docbase.xxxx/
These directories are used to save temp files during multipart upload among other needed functions. In production system such as Centos, by default system is configured to delete all /tmp
files if not touched for 10 days. Once this file is deleted logs will show this error and upload will fail with following exception
org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [**/tmp/tomcat.1220970741172837513.8080/work/Tomcat/localhost/ROOT]** is not valid
at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:111)
Possible Solution:
May be lets check if required temp directory doesn't exist recreate during multipart upload. And upload will work without any additional settings.
Other fixes/suggestions are welcome as well.
Affected Versions
Logs are from Spring Boot v1.4.3.RELEASE but its still same in 1.5.3.RELEASE
Workarounds
OR handle programmatically
GlobalException
Handler and recreate directory and inform user to try upload again.There's quite a bit of discussion on this in #5009. The documentation for 1.5.0 was also updated with our suggested solution.
@shehzadgee I know this and the other tickets have been closed, however without a working solution. Can you please elaborate a bit how I would implement your suggested solution of catching the exception and recreating the directory?
Generally, I do not think that something simple as storing a file in a temporary directory during upload should require manual action by the developer or server admin of any kind, so if you reconsider fixing the issue would be great. As for the discussion in #5009 I also think /tmp
is the correct directory.
@philwebb this doesn't seem to be a duplicate can it be reopened?
the other issues are about using another location and the danger of using /tmp
This one is about recreating the files if it can be detected that they have been deleted.
Hi Kappmeier,
I didn't get chance to look into spring boot code to find where this multipart exception is throw. But I handled it my GlobalExceptionHandler
in following way. So when I detect that an upload failed because directory is deleted by system cleanup process. I just recreate it and ask user to try upload file again.
So in my humble opinion same check can be placed inside Spring Boot so if directory doesn't exist, it should try to recreate. If it failed due to access rights only than upload should fail.
public ResponseEntity<?> handle(org.springframework.web.multipart.MultipartException exception) {
Log.error("handle->MultipartException" + exception.getMessage(),exception);
// general exception
if (exception.getCause() instanceof IOException && exception.getCause().getMessage().startsWith("The temporary upload location"))
{
String pathToRecreate = exception.getMessage().substring(exception.getMessage().indexOf("[")+1,exception.getMessage().indexOf("]"));
Set<PosixFilePermission> perms = new HashSet<>();
// add permission as rw-r--r-- 644
perms.add(PosixFilePermission.OWNER_WRITE);
perms.add(PosixFilePermission.OWNER_READ);
perms.add(PosixFilePermission.OWNER_EXECUTE);
perms.add(PosixFilePermission.GROUP_READ);
perms.add(PosixFilePermission.GROUP_WRITE);
perms.add(PosixFilePermission.GROUP_EXECUTE);
FileAttribute<Set<PosixFilePermission>> fileAttributes = PosixFilePermissions.asFileAttribute(perms);
try {
Files.createDirectories(FileSystems.getDefault().getPath(pathToRecreate), fileAttributes);
} catch (IOException e) {
LOG.error(e.getMessage(),e);
return ResponseUtils.sendError("Unable to recreate deleted temp directories. Please check "+ pathToRecreate);
}
return ResponseUtils.sendError("Recovered from temporary error by recreating temporary directory. Please try to upload logo again.");
}
return ResponseUtils.sendError("Unable to process this request.");
}
I'm still not too sure how we can fix this, but I'm happy to try again in the 2.0.x line. One option might be to periodically update a file in those directories so that they don't get deleted (see this comment). Another idea would be to write a special MultipartResolver
that tests if the directly exists before attempting to use it.
My program has no Multipart file upload task but it occurs error like:
org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.io.IOException: The temporary upload location [/tmp/tomcat.7104877156386249310.8070/work/Tomcat/localhost/ROOT] is not valid.
why it occurs really make me confused!
Another option would be to have a background thread that periodically writes a file to the folder. That would stop it from being deleted and it would fix other errors not related to file upload.
I encouter the same issue. In my case, i rebooted my application but it did not work and I tried to create the directory (tomcat.xxxx/work/../ROOT) manually it did not work.
Also My app do not have a funtion like uploading file. The exception occurrd when I use restTemplate (post). I do not understand why my spring app use the MultipartServlet..
Please help me out.
@sooyoung32 As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements. If you are looking for some help, please come and chat with the community on Gitter or post a question on Stack Overflow.
@wilkinsona Okay. Thank you for the answer. By the way My app work properly now after a day after rebooting my app. I guess the directory was cached in somewhere ...
I posted my question in Stackoverflow. (https://stackoverflow.com/questions/50523407/the-temporary-upload-location-tmp-tomcat-4296537502689403143-5000-work-tomcat)
I had the exact error on my application today. It said temporary upload location is not valid.
What I did to solve the issue was to relaunch the application adding -java.tmp.dir=/path/to/application/temp/ and creating a /temp/ folder in my application folder.
@VitalieS Yes, the bug is still open. If you look at the very top of the issue there's a little icon that tells you the current status.
@philwebb Has the issue been resolved in the 2.1.0 release version?
If the problem has not been resolved, can you tell me how corrective action is going on for that issue? :)
@ninezero90hy The issue is still open and there's not been any progress made on implementing a solution.
As of Tomcat 9.0.17 and 8.5.39, Tomcat will have opt-in support for creating the necessary directories when saving an upload. We can use this issue to opt in and update the documentation.
looking forwards to springboot's follow up.
i've seen apache had pushed the commit for this option (not released yet)
https://github.com/apache/tomcat/commit/267b8d8852db44dbad249453099ef6e9c26a4e9f
Hi,
I am still getting the same error and I am using <spring.boot.version>2.1.0.RELEASE</spring.boot.version>
.
On the GitHub spring boot releases page, I am seeing this bug fix was made to Spring v1.5.20.RELEASE
. But I don't see any corresponding fix in Spring Boot 2.x.x
. Can some one please help to what version of Spring Boot 2.x this fix is available and why I am not seeing it in release history or am I missing something.**
v1.5.20.RELEASE
@snicoll snicoll released this on Apr 3 · 10683 commits to master since this release
🐞 Bug Fixes
Tomcat does not create temporary directory used to store file uploads when it does not exist #9616
@dixgaurav You can see the list of releases that contain the fix in the commit that closed this issue. In the 2.1.x line it was fixed in 2.1.4.
@dixgaurav You can see the list of releases that contain the fix in the commit that closed this issue. In the 2.1.x line it was fixed in 2.1.4.
@wilkinsona Thanks for pointing out, I can see to all releases this fix commit went into!
Most helpful comment
@dixgaurav You can see the list of releases that contain the fix in the commit that closed this issue. In the 2.1.x line it was fixed in 2.1.4.