Spring MVC使用ResponseEntity文件下载,文件损坏问题

我们实现http文件下载,主要是在响应头中让Content-Disposition的值为attachment(意味着消息体应该被下载到本地;大多数浏览器会呈现一个“保存为”的对话框,将filename的值预填为下载后的文件名,假如它存在的话)

之前我都是使用在下载的方法里注入HttpServletResponse,然后往response里写数据,然后设置Header中加入Content-Disposition

今天刚好又需要写一个下载的功能,然而我不想注入什么HttpServletResponse,那样很不美观,从网上的资料了解到了ResponseEntity,从spring MVC的文档中了解到这个类就像@ResponseBody,但是多了响应头和状态码。刚好下载文件是需要设置响应头的,所以就用了这个。

然而构造方法是这样的

public ResponseEntity(T body, MultiValueMap headers, HttpStatus status) {
        super(body, headers);
        Assert.notNull(status, "HttpStatus must not be null");
        this.status = status;
    }

这个body能不能直接用File呢,答案是不能。我试着下载了一个txt文件,确实会下载一个文件,但是这个文件的内容是文件的路径。我猜估计是掉了File的toString方法。具体干了啥,感兴趣的可以看看AbstractMessageConverterMethodProcessorwriteWithMessageConverters,debug一遍就清楚了。

不能直接用File,那该用什么呢,习惯性的在stackoverflow上搜了一下,有用Resource的,也有用Byte[]的。那么两者有啥区别呢。
用Resource,你返回的时候还是依赖文件的,你的Controller在返回之后还要进行一系列的操作,如果在这之前文件被删了,那么下载下拉的文件就是损坏的。一开始我是这么干的

try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.valueOf("application/force-download"));
            headers.set("Content-Disposition", "attachment;fileName*=UTF-8''" + UriUtils.encode(file.getName(), "UTF-8"));
            return new ResponseEntity(new FileSystemResource(file), headers, HttpStatus.OK);
        } finally {
            // 这里删除文件
        }

这个finally会在return语句的计算之后返回之前执行。所以文件被写到响应之前就被删了。所以我决定采用这种方式

 try(FileInputStream fi = new FileInputStream(file)) {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.valueOf("application/force-download"));
            headers.set("Content-Disposition", "attachment;fileName*=UTF-8''" + UriUtils.encode(file.getName(), "UTF-8"));
            return new ResponseEntity(IOUtils.toByteArray(fi), headers, HttpStatus.OK);
        } finally {
            // 这里删除文件
        }

文件流要关闭哟,希望大家都用try-with-resources,这都9102年了答应我不要在finally里面close然后又try-catch了,我实在看不下去了

你可能感兴趣的:(Spring MVC使用ResponseEntity文件下载,文件损坏问题)