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
- On Centos 7 update tmp.conf in following three directories /etc/tmpfiles.d/, /run/tmpfiles.d/ and /usr/lib/tmpfiles.d/. with following line x /tmp/tomcat*
OR handle programmatically
- Catch exception in
GlobalExceptionHandler and recreate directory and inform user to try upload again.
Comment From: philwebb
There's quite a bit of discussion on this in #5009. The documentation for 1.5.0 was also updated with our suggested solution.
Comment From: kappmeier
@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.
Comment From: timja
@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.
Comment From: shehzadgee
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.");
}
Comment From: philwebb
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.
Comment From: jintaoit
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!
Comment From: philwebb
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.
Comment From: sooyoung32
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.
Comment From: wilkinsona
@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.
Comment From: sooyoung32
@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)
Comment From: VitalieS
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.
Comment From: philwebb
@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.
Comment From: ninezero90hy
@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? :)
Comment From: philwebb
@ninezero90hy The issue is still open and there's not been any progress made on implementing a solution.
Comment From: wilkinsona
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.
Comment From: timmyz
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
Comment From: dixgaurav
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
Comment From: wilkinsona
@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.
Comment From: dixgaurav
@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!
Comment From: hendisantika
Is it solved? I still have this issue when upload multipart csv file.
Repository https://github.com/hendisantika/springboot-multithreading
What's is the solution?
Thanks
Comment From: jintaoit
您好,你的邮件我已经收到,我会尽快回复你,谢谢!
Comment From: wilkinsona
@hendisantika Spring Boot should have configured Tomcat to create the directory to which a file upload will be written. If that's not working for you, please open a new issue with a complete yet minimal sample that reproduces the problem. I took a quick look at the repository that you've already shared and it wasn't clear to me what to do to reproduce the problem. It's also trying to use Spring Boot 2.7.0 which has not yet been released.
Comment From: hendisantika
Yes, i put Spring Boot 2.7.0 version. So that I don't have to update my github again.
OK. I will create new issue then @wilkinsona
Comment From: sazonov
Same problem with Undertow, is it possible to adjust the fix?
Comment From: wilkinsona
@sazonov If you open a new issue with a small sample project that reproduces the problem with Undertow we can take a look.
Comment From: ShubhamGupta9582
Is it fully resolved?
Getting similar issue. I am reading input stream from MultipartFile and hitting another REST API in a loop. And initially for sometime it works fine but after some time it starts giving FileNotFoundException.
Let's say, I am running loop for 10 times. So, for 6-7 times it works fine but after that it starts giving this error.
I am using spring boot version 2.4.5
Exception:-
java.io.FileNotFoundException: /tmp/tomcat.8080.4399610540988131483/work/Tomcat/localhost/ROOT/upload_d04b3ab7_7d5b_405e_9a50_d0fe0a8c9e6f_00000000.tmp (No such file or directory)
I have also tried spring.servlet.multipart.location property to change default multipart file upload location. In this case, it creates new temporary location and works for some time initially but after some time it starts giving the same error. (Let's say, I am running loop for 10 times. So, for 6-7 times it works fine but after that it starts giving FileNotFoundException. )
Any idea why am i getting this error ?
Comment From: wilkinsona
@ShubhamGupta9582 Support for Spring Boot 2.4.x ended in November 2021. Please upgrade to Spring Boot 2.6.x or 2.7.x. If you are then still experiencing the same problem and would like us to investigate further, please open a new issue with a minimal sample that reproduces the problem.
Comment From: jintaoit
您好,你的邮件我已经收到,我会尽快回复你,谢谢!
Google translation:
Hello, I have received your email, I will reply to you as soon as possible, thank you!
Comment From: jintaoit
您好,你的邮件我已经收到,我会尽快回复你,谢谢!