### Do
1. Return `ResponseEntity<Resource>` from a handler method
1. Specify `Content-Type`
1. Set `Content-Disposition` if necessary:
1. filename
1. type
1. [`inline`](
[To see links please register here]
) to force preview in a browser
1. [`attachment`](
[To see links please register here]
) to force a download
### Example
```java
@Controller
public class DownloadController {
@GetMapping("/downloadPdf.pdf")
// 1.
public ResponseEntity<Resource> downloadPdf() {
FileSystemResource resource = new FileSystemResource("/home/caco3/Downloads/JMC_Tutorial.pdf");
// 2.
MediaType mediaType = MediaTypeFactory
.getMediaType(resource)
.orElse(MediaType.APPLICATION_OCTET_STREAM);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
// 3
ContentDisposition disposition = ContentDisposition
// 3.2
.inline() // or .attachment()
// 3.1
.filename(resource.getFilename())
.build();
headers.setContentDisposition(disposition);
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
}
}
```
### Explanation
**Return `ResponseEntity<Resource>`**
When you return a [`ResponseEntity<Resource>`](
[To see links please register here]
), the [`ResourceHttpMessageConverter`](
[To see links please register here]
) writes file contents
Examples of `Resource` implementations:
* [`ByteArrayResource`](
[To see links please register here]
) - based in `byte[]`
* [`FileSystemResource`](
[To see links please register here]
) - for a `File` or a `Path`
* [`UrlResource`](
[To see links please register here]
) - retrieved from `java.net.URL`
* [`GridFsResource`](
[To see links please register here]
) - a blob stored in MongoDB
* [`ClassPathResource`](
[To see links please register here]
) - for files in classpath, for example files from `resources` directory. [My answer to question "Read file from resources folder in Spring Boot"](
[To see links please register here]
) explains how to locate the resource in classpath in details
**Specify `Content-Type` explicitly**:
_Reason: see "[FileSystemResource is returned with content type json](
[To see links please register here]
; question_
Options:
* Hardcode the header
* Use the [`MediaTypeFactory`](
[To see links please register here]
) from Spring. The `MediaTypeFactory` maps `Resource` to `MediaType` using the `/org/springframework/http/mime.types` file
* Use a third party library like [Apache Tika](
[To see links please register here]
)
**Set `Content-Disposition` if necessary**:
About [`Content-Disposition`](
[To see links please register here]
) header:
> The first parameter in the HTTP context is either `inline` (default value, indicating it can be displayed inside the Web page, or as the Web page) or `attachment` (indicating it should be downloaded; most browsers presenting a 'Save as' dialog, prefilled with the value of the filename parameters if present).
Use [`ContentDisposition`](
[To see links please register here]
) in application:
* To *preview* a file in a browser:
```java
ContentDisposition disposition = ContentDisposition
.inline()
.filename(resource.getFilename())
.build();
```
* To force a *download*:
```java
ContentDisposition disposition = ContentDisposition
.attachment()
.filename(resource.getFilename())
.build();
```
**Use `InputStreamResource` carefully**:
Specify `Content-Length` using the [`HttpHeaders#setContentLength`](
[To see links please register here]
) method if:
1. The length is known
2. You use `InputStreamResource`
_Reason: Spring won't write `Content-Length` for `InputStreamResource` because Spring can't determine the length of the resource. Here is a snippet of code from `ResourceHttpMessageConverter`_:
```java
@Override
protected Long getContentLength(Resource resource, @Nullable MediaType contentType) throws IOException {
// Don't try to determine contentLength on InputStreamResource - cannot be read afterwards...
// Note: custom InputStreamResource subclasses could provide a pre-calculated content length!
if (InputStreamResource.class == resource.getClass()) {
return null;
}
long contentLength = resource.contentLength();
return (contentLength < 0 ? null : contentLength);
}
```
In other cases Spring sets the `Content-Length`:
```bash
~ $ curl -I localhost:8080/downloadPdf.pdf | grep "Content-Length"
Content-Length: 7554270
```