I'm trying to serve large temporary files from Spray. I need to delete those files once HTTP request is complete. I could not find a way to do this so far...
I'm using code similar to this or this:
respondWithMediaType(`text/csv`) {
path("somepath" / CsvObjectIdSegment) {
id =>
CsvExporter.export(id) { // loan pattern to provide temp file for this request
file =>
encodeResponse(Gzip) {
getFromFile(file)
}
}
}
}
So essentially it calls getFromFile
which completes the route in a Future
. The problem is that even if that Future
is complete the web request is not complete yet. I tried to write a function similar to getFromFile
and I would call file.delete()
in onComplete
of that Future
but it has the same problem - Future
completes before the client finished downloading the file if the file is large enough.
Here is getFromFile
from Spray for reference:
/**
* Completes GET requests with the content of the given file. The actual I/O operation is
* running detached in a `Future`, so it doesn't block the current thread (but potentially
* some other thread !). If the file cannot be found or read the request is rejected.
*/
def getFromFile(file: File)(implicit settings: RoutingSettings,
resolver: ContentTypeResolver,
refFactory: ActorRefFactory): Route =
getFromFile(file, resolver(file.getName))
I can't use file.deleteOnExit()
because JVM might not be restarted for a while and temp files will be kept laying around wasting disk space.
On the other hand it's a more general question - is there a way to install a callback in Spray so that when request processing is complete resources can be released or statistics/logs can be updated, etc.
Thanks to @VladimirPetrosyan for the pointer. Here is how I implemented it:
The route has this:
and the trait that I mix in that does the unmarshalling producing chunked response:
The temp file is created and then actor starts streaming chunks. It sends a chunk whenever response from client is received. Whenever client disconnects temp file is deleted and actor is shut down.
This requires you to run your app in spray-can and I think will not work if you run it in container.
Some useful links: example1, example2, docs