Caution when using Play's TemporaryFile

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 (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.

Stay connected

I send out occasional updates on posts, interesting finds, and projects I'm working on. I'd love to include you. No tracking, one-click unsubscribe.