【问题标题】:Spring Boot Callback after client receives resource?客户端收到资源后的Spring Boot回调?
【发布时间】:2019-04-01 21:10:11
【问题描述】:

我正在使用 Spring Boot 创建一个端点,该端点执行系统命令 (java.lang.Runtime API) 的组合以生成一个 zip 文件以根据请求返回给客户端,代码如下。

    @GetMapping(value = "generateZipFile")
    public ResponseEntity<Resource> generateZipFile(@RequestParam("id") Integer id) throws IOException {
        org.springframework.core.io.Resource resource = null;
        //generate zip file using commandline 

        resource = service.generateTmpResource(id);
        return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_TYPE, "application/zip")
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"randomFile.zip\"")
                    .body(resource);
        //somehow delete generated file here after client receives it 
    }

由于明显的磁盘限制原因,我无法继续在服务器上堆积文件,因此我正在寻找一种在客户端下载文件后立即删除文件的方法。 Spring Boot中有解决方案吗?我基本上需要挂钩一个回调,在用户收到资源后进行清理。

我使用的是 Spring Boot 2.0.6

【问题讨论】:

    标签: spring-boot


    【解决方案1】:

    您可以创建一个新线程,但最好的解决方案是创建一个 ThreadPoolExecutor 来管理线程,或者 Scheduled annotation 也可以帮助我们。

    new Thread() {
        @Override
        public void run() {
            service.cleanup(id);
        }
    }.start();
    

    更新

    最佳答案是,将 StackThread 结合使用。 这是我已经完成的解决方案。

    https://github.com/jjohxx/example-thread

    【讨论】:

    • 我已经完成了该解决方案,以便为“清理”运行一个新线程,因为当您保持在同一个线程上时,您将在同一个线程上清理一个您应该稍后删除的文件。这是因为您收到的是一个空文件。
    • 什么保证您的线程在原始线程完成文件服务之前不会删除资源?我发布了一个带有有效解决方案的答案。
    • 您可以等待当前线程完成并运行这个新线程,所以我提供像 ThreadPoolExecutor 这样的想法可以帮助您管理线程。
    • 这是我一直在与您交谈的示例 github.com/jjohxx/example-thread 。你可以看看你得到响应之后的那一刻,你想做的事情就会被执行。 @ALTN
    【解决方案2】:

    我最终使用了HandlerInterceptorAdapterafterCompletion 是我需要的回调。我必须处理的唯一挑战是通过资源的 id 进行清理,我通过在我的控制器方法中添加一个标头来处理它:

    @GetMapping(value = "generateZipFile")
    public ResponseEntity<Resource> genereateZipFile(@RequestParam("id") Integer id,
                                                     RedirectAttributes redirectAttributes) throws IOException {
        org.springframework.core.io.Resource resource = myService.generateTmpResource(id);;
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_TYPE, "application/zip")
                .header(MyInterceptor.TMP_RESOURCE_ID_HEADER, id.toString())
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"someFile.zip\"")
                .body(resource);
    
    }
    

    拦截器代码:

    @Component
    public class MyInterceptor extends HandlerInterceptorAdapter {
    
        public static final String TMP_RESOURCE_ID_HEADER = "Tmp-ID";
    
        @Autowired
        private MyService myService;
    
        @Override
        public void afterCompletion(HttpServletRequest request,
                                    HttpServletResponse response,
                                    Object handler,
                                    Exception ex) {
            if(response == null || !response.containsHeader(TMP_RESOURCE_ID_HEADER)) return;
            String tmpFileId = response.getHeader(TMP_RESOURCE_ID_HEADER);
            myService.cleanup(tmpFileId);
        }
    }
    

    有关拦截器的更多信息,请参阅here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-17
      • 2015-08-26
      • 2022-11-28
      • 2021-10-02
      • 2019-04-24
      • 2015-01-01
      • 2019-06-29
      • 2018-08-22
      相关资源
      最近更新 更多