Context:
I have an endpoint that is used to download a zip archive.
@GetMapping
public DeferredResult<StreamingResponseBody> download(/**params**/) {
}
Due to the number of files and file sizes, keeping all of them in memory is not possible (i.e. I have to read chunks of files from an external service, store them to a temporary directory, create a zip archive, write files in chunks to the zip, delete all temporary files after the archiving is done - at this point I am left only with the archive on disk - and afterwards stream the zip back to the client).
I've managed to implement the functionality, but I am not sure which is the best way to clean up after a request (note that the service is used by multiple clients - so multiple downloads should be able to happen simultaneously).
Currently, I am using a HandlerInterceptor that generates a random name for the archive in the preHandle method and passes it along as a request attribute. The request attribute is passed along to the service that generates the archive and is used as archive name. Then, in the afterCompletion method of the interceptor, I read the archive name from the request attribute and delete the archive.
class ZipInterceptor implements HandlerInterceptor {
public static final String ZIP_ATTRIBUTE_NAME = "zipName";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(isApplicable(request)){
request.setAttribute(ZIP_ATTRIBUTE_NAME, generateZipName());
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
if(isApplicable(request)){
deleteZip((String)request.getAttribute(ZIP_ATTRIBUTE_NAME));
}
}
}
Another approaches that I've considered:
- Returning the archive name to the client (as a header or as the name of the downloaded file) and relying on the client to call a new endpoint to delete the archive. The main drawback is that I have no way of forcing the clients into using this protocol (and might end up with the disk full).
- Run a job every hour (or another time interval) and clean old archives. The drawback is that I don't know for sure weather a file finished streaming back to the clients or not (so for a big enough file any time interval will delete a file that is not finished streaming yet).
Which would be, in your opinion, the best way to handle this scenario?