Play! Framework provides several
BodyParsers out of the box to parse common HTTP request bodies. One of them is for file uploads. Since Play can’t reasonably know what you’re doing with the body, they can’t aggressively delete the file after the result is returned. Their solution is to clean up the file when the variable is garbage collected, using Java’s finalize—rarely seen in the Scala world. Since the
TemporaryFile is held inside
request.body: TemporaryFile), this is usually reasonable since
request won’t be GCed during the lifecycle of the request.
However, this can bite you when doing asynchronous file operations, such as image resizing then uploading to a CDN. Since your APIs typically deal in real
Files (instead of the wrapped TemporaryFile), you may assume you can safely pass
request.body.file around. As soon as request goes out of scope (usually, by returning to the client), that
File reference can be deleted out from under you. Since it’s garbage collection that triggers this, it probably won’t happen often, and will happen somewhat randomly.
Recently, I ran into this when trying to determine why around 1% of image uploads (which involve resizing to various sizes and uploading to S3) on Kifi were failing. Unfortunately, the file deletion seemed to always happen between two lines that couldn’t have deleted it. After a couple hours, we noticed TemporaryFile’s documented behavior. Sigh.
So, be very careful with TemporaryFile. If you would like Play to manage the File lifecycle, make sure you’re done before
request.file can be GCed. If not, make a copy immediately, and then assume responsibility for file management yourself.
I would have preferred that Play immediately delete the file after the
Action returns, so at least behavior is consistent.