将响应返回给客户端后清理

时间:2018-09-18 13:10:35

标签: java spring spring-boot

上下文:

我有一个用于下载zip存档的端点。

@GetMapping
public DeferredResult<StreamingResponseBody> download(/**params**/) {

}

由于文件数和文件大小的原因,无法将它们全部保存在内存中(即,我必须从外部服务读取大块文件,将它们存储到临时目录中,创建一个zip存档,写入文件归档后,请删除所有临时文件-这时,我只剩下磁盘上的存档文件-然后将zip传输回客户端)。

我已经设法实现了该功能,但是我不确定哪种方法是在请求后进行清理的最佳方法(请注意,该服务由多个客户端使用-因此应该可以同时进行多个下载)。

当前,我正在使用HandlerInterceptor,该方法通过preHandle方法为存档生成一个随机名称,并将其作为请求属性传递。 request属性将传递给生成档案的服务,并用作档案名称。然后,在拦截器的afterCompletion方法中,我从request属性中读取档案名称并删除档案。

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));
        }
    }
}

我考虑过的另一种方法:

  • 将存档名称返回给客户端(作为标头或下载文件的名称),并依靠客户端调用新的端点来删除存档。主要缺点是我无法强制客户端使用此协议(并且可能最终会导致磁盘已满)。
  • 每小时(或每隔一个时间间隔)运行一次作业,并清理旧存档。缺点是我不确定是否将文件完成流传输回客户端(因此,对于足够大的文件,任何时间间隔都会删除尚未完成流传输的文件)。

您认为哪种方法是应对这种情况的最佳方法?

2 个答案:

答案 0 :(得分:1)

正如JB Nizet在评论中指出的那样,最好的方法是完全不将文件存储在磁盘上

如果在请求返回给客户端之后,如果您的特定上下文在磁盘上留下文件,则可以使用下一种方法来删除它们。

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
    }
}

如果有人找到解决此问题的更优雅的解决方案,请在此处发布您的答案。

答案 1 :(得分:0)

我也有类似的情况。我认为最安全的方法是创建一个后台作业来清理文件。我在创建的文件名中添加了时间戳。然后后台作业将过滤掉比当前时间早至少 30 分钟的文件并删除它们。这样我就可以确定这些文件已经完成流式传输并且可以安全删除