We implement HTTP file downloads primarily by making content-disposition the value of attachment in the response header (meaning that the message body should be downloaded locally; Most browsers will present a “save as” dialog box, prefilling the value of filename with the downloaded filename, if it exists.)

I used to inject HttpServletResponse in the download method, then write the data in response, and then set content-disposition in the Header

I happen to need to write a download function today, but I don’t want to inject any HttpServletResponse, that would be ugly, and I know that ResponseEntity from the web, I know from the spring MVC documentation that this class is like @responseBody, But there are more response headers and status codes. It just so happens that downloading the file requires setting the response header, so we use this.

However, the constructor looks like this

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

Can I just use File for the body? The answer is no. I tried to download a TXT file, it does download a file, but the content of the file is the path of the file. I guess I dropped the toString method on File. Specific stem what, interested can look at AbstractMessageConverterMethodProcessor writeWithMessageConverters, debug it again.

[] [/ b] [/ b] [/ b] [/ b] [/ b] [/ b] [/ b] [/ b] [/ b] [/ b] [/ b] So what’s the difference? With Resource, you still rely on the file when you return, and your Controller has to do a bunch of things after you return, and if the file is deleted before that, then the downloaded file is corrupted. This is what I did in the beginning

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"));
            returnnew ResponseEntity<Resource>(new FileSystemResource(file), headers, HttpStatus.OK); } finally {// delete file here}Copy the code

The finally is executed before the return statement returns after the evaluation. So the file was deleted before it was written to the response. So I decided to do it this way

 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<byte[]>(IOUtils.toByteArray(fi), headers, HttpStatus.OK); } finally {// delete file here}Copy the code

In the end, try-with-resources are used to close the file stream. In the end, try-with-resources are used to close the file stream