Clean up after response is returned to client

Viewed 563

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?

2 Answers

As JB Nizet pointed out in the comments, the best approach would be to not store the files on disk at all.

If your specific context left you with files on disk after the request is returned to the client, you could use the next approach to get rid of them.

class CleanupInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        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 {
        // do cleanup here
    }
}

If anyone finds a more elegant solution to tackle this, please post your answer here.

I have similar situation. I think the most safest way is to go with creating a background job to clean up the files. I added a timestamp to the file name that I created. Then the background job will filter out the files that are at least 30 mins older than current time and delete them. That way I know for sure that those files are already finished streaming and safe to delete

Related