I'm getting image data (as byte[]) from DB. How to return this image in #ResponseBody ?
EDIT
I did it without #ResponseBody using HttpServletResponse as method parameter:
#RequestMapping("/photo1")
public void photo(HttpServletResponse response) throws IOException {
response.setContentType("image/jpeg");
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
IOUtils.copy(in, response.getOutputStream());
}
Using #ResponseBody with registered org.springframework.http.converter.ByteArrayHttpMessageConverter converter as #Sid said doesn't work for me :(.
#ResponseBody
#RequestMapping("/photo2")
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
if you are using Spring version of 3.1 or newer you can specify "produces" in #RequestMapping annotation. Example below works for me out of box. No need of register converter or anything else if you have web mvc enabled (#EnableWebMvc).
#ResponseBody
#RequestMapping(value = "/photo2", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
With Spring 4.1 and above, you can return pretty much anything (such as pictures, pdfs, documents, jars, zips, etc) quite simply without any extra dependencies. For example, the following could be a method to return a user's profile picture from MongoDB GridFS:
#RequestMapping(value = "user/avatar/{userId}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<InputStreamResource> downloadUserAvatarImage(#PathVariable Long userId) {
GridFSDBFile gridFsFile = fileService.findUserAccountAvatarById(userId);
return ResponseEntity.ok()
.contentLength(gridFsFile.getLength())
.contentType(MediaType.parseMediaType(gridFsFile.getContentType()))
.body(new InputStreamResource(gridFsFile.getInputStream()));
}
The things to note:
ResponseEntity with InputStreamResource as a return type
ResponseEntity builder style creation
With this method you dont have to worry about autowiring in the HttpServletResponse, throwing an IOException or copying stream data around.
In addition to registering a ByteArrayHttpMessageConverter, you may want to use a ResponseEntity instead of #ResponseBody. The following code works for me :
#RequestMapping("/photo2")
public ResponseEntity<byte[]> testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
}
By using Spring 3.1.x and 3.2.x, this is how you should do it:
The controller method:
#RequestMapping("/photo2")
public #ResponseBody byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
And the mvc annotation in servlet-context.xml file:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>image/jpeg</value>
<value>image/png</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
In addition to a couple of answers here a few pointers (Spring 4.1).
Incase you don't have any messageconverters configured in your WebMvcConfig, having ResponseEntity inside your #ResponseBody works well.
If you do, i.e. you have a MappingJackson2HttpMessageConverter configured (like me) using the ResponseEntity returns a org.springframework.http.converter.HttpMessageNotWritableException.
The only working solution in this case is to wrap a byte[] in the #ResponseBody as follows:
#RequestMapping(value = "/get/image/{id}", method=RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
public #ResponseBody byte[] showImageOnId(#PathVariable("id") String id) {
byte[] b = whatEverMethodUsedToObtainBytes(id);
return b;
}
In this case do rememeber to configure the messageconverters properly (and add a ByteArrayHttpMessageConverer) in your WebMvcConfig, like so:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
converters.add(byteArrayHttpMessageConverter());
}
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper);
return converter;
}
#Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
return arrayHttpMessageConverter;
}
private List<MediaType> getSupportedMediaTypes() {
List<MediaType> list = new ArrayList<MediaType>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.IMAGE_PNG);
list.add(MediaType.APPLICATION_OCTET_STREAM);
return list;
}
I prefere this one:
private ResourceLoader resourceLoader = new DefaultResourceLoader();
#ResponseBody
#RequestMapping(value = "/{id}", produces = "image/bmp")
public Resource texture(#PathVariable("id") String id) {
return resourceLoader.getResource("classpath:images/" + id + ".bmp");
}
Change the media type to what ever image format you have.
In your application context declare a AnnotationMethodHandlerAdapter and registerByteArrayHttpMessageConverter:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list>
<bean id="byteArrayMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
</util:list>
</property>
</bean>
also in the handler method set appropriate content type for your response.
#RequestMapping(value = "/get-image",method = RequestMethod.GET)
public ResponseEntity<byte[]> getImage() throws IOException {
RandomAccessFile f = new RandomAccessFile("/home/vivex/apache-tomcat-7.0.59/tmpFiles/1.jpg", "r");
byte[] b = new byte[(int)f.length()];
f.readFully(b);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(b, headers, HttpStatus.CREATED);
}
Worked For Me.
You should specify the media type in the response. I'm using a #GetMapping annotation with produces = MediaType.IMAGE_JPEG_VALUE. #RequestMapping will work the same.
#GetMapping(value="/current/chart",produces = MediaType.IMAGE_JPEG_VALUE)
#ResponseBody
public byte[] getChart() {
return ...;
}
Without a media type, it is hard to guess what is actually returned (includes anybody who reads the code, browser and of course Spring itself). A byte[] is just not specific. The only way to determine the media type from a byte[] is sniffing and guessing around.
Providing a media type is just best practice
It's work for me in Spring 4.
#RequestMapping(value = "/image/{id}", method = RequestMethod.GET)
public void findImage(#PathVariable("id") String id, HttpServletResponse resp){
final Foto anafoto = <find object>
resp.reset();
resp.setContentType(MediaType.IMAGE_JPEG_VALUE);
resp.setContentLength(anafoto.getImage().length);
final BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(anafoto.getImageInBytes()));
try {
FileCopyUtils.copy(in, resp.getOutputStream());
resp.flushBuffer();
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Non of the answers worked for me, so I've managed to do it like that:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("your content type here"));
headers.set("Content-Disposition", "attachment; filename=fileName.jpg");
headers.setContentLength(fileContent.length);
return new ResponseEntity<>(fileContent, headers, HttpStatus.OK);
Setting Content-Disposition header I was able to download the file with the #ResponseBody annotation on my method.
This is how I do it with Spring Boot and Guava:
#RequestMapping(value = "/getimage", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public void getImage( HttpServletResponse response ) throws IOException
{
ByteStreams.copy( getClass().getResourceAsStream( "/preview-image.jpg" ), response.getOutputStream() );
}
In spring 4 it's very easy you don't need to make any changes in beans. Only mark your return type to #ResponseBody.
Example:-
#RequestMapping(value = "/image/{id}")
public #ResponseBody
byte[] showImage(#PathVariable Integer id) {
byte[] b;
/* Do your logic and return
*/
return b;
}
I think you maybe need a service to store file upload and get that file.
Check more detail from here
1) Create a Storage Sevice
#Service
public class StorageService {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private final Path rootLocation = Paths.get("upload-dir");
public void store(MultipartFile file) {
try {
Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
} catch (Exception e) {
throw new RuntimeException("FAIL!");
}
}
public Resource loadFile(String filename) {
try {
Path file = rootLocation.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("FAIL!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("FAIL!");
}
}
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
public void init() {
try {
Files.createDirectory(rootLocation);
} catch (IOException e) {
throw new RuntimeException("Could not initialize storage!");
}
}
}
2) Create Rest Controller to upload and get file
#Controller
public class UploadController {
#Autowired
StorageService storageService;
List<String> files = new ArrayList<String>();
#PostMapping("/post")
public ResponseEntity<String> handleFileUpload(#RequestParam("file") MultipartFile file) {
String message = "";
try {
storageService.store(file);
files.add(file.getOriginalFilename());
message = "You successfully uploaded " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.OK).body(message);
} catch (Exception e) {
message = "FAIL to upload " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message);
}
}
#GetMapping("/getallfiles")
public ResponseEntity<List<String>> getListFiles(Model model) {
List<String> fileNames = files
.stream().map(fileName -> MvcUriComponentsBuilder
.fromMethodName(UploadController.class, "getFile", fileName).build().toString())
.collect(Collectors.toList());
return ResponseEntity.ok().body(fileNames);
}
#GetMapping("/files/{filename:.+}")
#ResponseBody
public ResponseEntity<Resource> getFile(#PathVariable String filename) {
Resource file = storageService.loadFile(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
.body(file);
}
}
When using produces with MediaType.IMAGE_JPEG_VALUE, make sure that you are returning byte[], but not Byte[]. Very strange, but spring cannot convert it and raises an exception: no converter found.
Related
I have a method that sends a rest request to an api with multipart-formdata, this will upload a file to the external api. However, I am not able to finish the unit test method for this.
The first problem I am finding is that the content-type that I am expecting is always different than the one that method create. For some reason when sending the request the mediatype is multipart-formdata but the header is set as that in addition to charset and boundary. The latter, boundary, is always changing its value therefore I can not set the expected value on the unit tests because it will always be different.
Apart from that, how do I also expect that the content of the request is the same content that I initiated the test with? How do I assert that the payload is the same.
Please check the code:
Service class:
#Service
#Slf4j
public class JiraService {
private HttpHeaders createRequestHeaders(JiraClient jiraClient, MediaType contenType) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(contenType);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setBasicAuth(jiraClient.getUsername(), jiraClient.getPassword());
return headers;
}
private <EC, RC> ResponseEntity<RC> createRequestAndSend(HttpMethod method, String url, HttpHeaders headers,
EC payload, Class<RC> responseType) {
HttpEntity<EC> requestEntity = new HttpEntity<>(payload, headers);
ResponseEntity<RC> responseEntity = restTemplate.exchange(url, method, requestEntity, responseType);
// TODO deal with response
log.error("Loggin something");
return responseEntity;
}
public void addAttachment(JiraClient jiraClient, JiraIssue jiraIssue, JiraAttachment jiraAttachment)
throws MalformedURLException, IOException {
String url = jiraClient.getHost() + "/rest/api/2/issue/" + jiraIssue.getKey() + "/attachments";
HttpHeaders headers = createRequestHeaders(jiraClient, MediaType.MULTIPART_FORM_DATA); // What to do here?
headers.set("X-Atlassian-Token", "no-check");
FileSystemResource file = jiraAttachment.downloadFileFromWeb();
MultiValueMap<String, Object> payload = new LinkedMultiValueMap<>();
payload.add("file", file);
createRequestAndSend(HttpMethod.POST, url, headers, payload, String.class);
jiraAttachment.deleteFileFromSystem();
}
}
ServiceTest.class
#ActiveProfiles("test")
#RestClientTest(JiraService.class)
public class JiraServiceTest {
#Value("classpath:jira/add_attachment/validJiraAttachmentAddition.json")
private Resource validJiraAttachmentAddition;
#Autowired
private MockRestServiceServer server;
#Autowired
private JiraService jiraService;
#Mock
private JiraAttachment mockJiraAttachment;
private FileSystemResource attachmentFileSystemResource;
#BeforeEach
public void setupTests() throws IOException {
// initialize mocks
}
#Test
public void addAttachment_WithValidData_ShouldAddAttachmentToJiraIssue() throws Exception {
String url = host + "/rest/api/2/issue/" + issueKey + "/attachments";
ResponseActions stub = createServiceStub(HttpMethod.POST, url, MediaType.MULTIPART_FORM_DATA_VALUE);
stub = stub.andExpect(header("X-Atlassian-Token", "no-check"));
stub.andRespond(withSuccess());
// How to assert that the content of the request is the same as the resource?
when(mockJiraAttachment.downloadFileFromWeb()).thenReturn(attachmentFileSystemResource);
jiraService.addAttachment(mockJiraClient, mockJiraIssue, mockJiraAttachment);
}
private ResponseActions createServiceStub(HttpMethod method, String url, String contenType) {
String encodedCredentials = Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
ResponseActions stub = server.expect(ExpectedCount.once(), requestTo(url));
stub = stub.andExpect(method(method));
stub = stub.andExpect(header("Content-Type", contenType)); // How to expect the content type here ?
stub = stub.andExpect(header("Authorization", "Basic " + encodedCredentials));
return stub;
}
}
Use ContentRequestMatchers.contentTypeCompatibleWith(MediaType contentType)
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
...
stub.andExpect(content().contentTypeCompatibleWith(MediaType.MULTIPART_FORM_DATA))
I'm trying to download a file using my restController, but it always return this error :
java.io.FileNotFoundException: Byte array resource [resource loaded from byte array] cannot be resolved to URL
at org.springframework.core.io.AbstractResource.getURL(AbstractResource.java:90) ~[spring-core-4.2.2.RELEASE.jar:4.2.2.RELEASE]
then it downloads a file that has something like this :
{"byteArray":"JVBERi0xLjQKJeL....
here's my restController :
#Api("products")
#RestController
#RequestMapping("/v1/products")
public class DocumentApi extends storeApi {
#ApiOperation("GET download document")
#RequestMapping(value = "/temp", method = RequestMethod.GET)
#ResponseStatus(code = HttpStatus.OK)
public ResponseEntity<ByteArrayResource> downloadDocument(
#RequestParam(value = "id", required = true) Long idInscription) throws IOException {
String signedFilePAth = "C:/APPLIS/signedTemp/5982312957957647037_signed.pdf"
return ResponseEntity
.ok()
.contentLength(contentLength)
.contentType(
MediaType.parseMediaType("application/pdf"))
.body(new
ByteArrayResource(Files.readAllBytes(Paths.get(signedFilePAth))));
}
}
and here's my spring configuration :
protected MappingJackson2HttpMessageConverter jacksonMessageConverter() {
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// Registering Hibernate4Module to support lazy objects
mapper.registerModule(new Hibernate4Module());
messageConverter.setObjectMapper(mapper);
return messageConverter;
}
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
return arrayHttpMessageConverter;
}
private List<MediaType> getSupportedMediaTypes() {
List<MediaType> list = new ArrayList<MediaType>();
list.add(MediaType.APPLICATION_OCTET_STREAM);
list.add(MediaType.parseMediaType("application/pdf"));
return list;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jacksonMessageConverter());
converters.add(byteArrayHttpMessageConverter());
super.configureMessageConverters(converters);
}
what seems to be the problem ? how can i solve this ?
nb : I don't know if this is related, but my swagger-ui.html doesn't work (it shows a blank page) while v2/api-docs/ works fine
Try returning array of bytes:
#RequestMapping(value = "/temp", method = RequestMethod.GET)
public #ResponseBody byte[] downloadDocument(
#RequestParam(value = "id", required = true) Long idInscription) throws IOException {
FileInputStream signedFileInputStream = new FileInputStream(signedFilePAth);
byte[] doc = IOUtils.toByteArray(fis);
return doc;
}
IOUtils is from org.apache.commons.io.IOUtils.
It seems that the response content type definition is missing from your code.
The following code snippet returns with an image content which is displayed by web browser. It is a Jersy code but you can adopt it to Spring:
#GET
#Path("/{image-uuid}")
#Produces("images/jpg")
public Response getImage(#PathParam("uuid") final String uuid) throws IOException {
byte[] content = imageDao.getImage(uuid);
if (Objects.isNull(content )) {
throw new ImageNotFoundError(uuid);
}
ByteArrayInputStream stream = new ByteArrayInputStream(content);
return Response.ok(stream).build();
}
Sorry, i am newbie on java web development.
I got some task to fetch user profile picture from 3rd party company via HTTP rest(GET method). Their api only can be accessed using session id on the header parameter and the api will return some byte[] array looks like ’ÑÒBRSb¢ÂáTr²ñ#‚4“â3C etc.
How to handle rest response with content type image/jpg in Rest Template?
I do my best like this
private RestTemplate restTemplate;
public byte[] getProfilePic(){
String canonicalPath = "http://dockertest/bankingapp/customer/profpicFile";
String sessionId= "MTQ4NzE5Mz...etc";
HttpEntity<byte[]> request = new HttpEntity<byte[]>(null, getHeaders(true, "GET", null, canonicalPath, sessionId));
//getHeaders() will return HttpHeaders with those parameter
ResponseEntity<byte[]> response = null;
try {
response = this.restTemplate.exchange(uri, HttpMethod.GET, request, byte[].class);
} catch( HttpServerErrorException hse ){
throw hse;
}
return response;
}
This code will return an error
org.springframework.web.client.RestClientException: Could not extract
response: no suitable HttpMessageConverter found for response type
[[B] and content type [image/jpg]
Any suggestion or help will be appreciated!
Thank you
Update
Using stackoveflower suggestions i can manage to solve this.
private RestTemplate restTemplate;
public byte[] getProfilePic(){
String canonicalPath = "/mobile/customer/profpicFile";
String sessionId= "MTQ4NzE5Mz...etc";
HttpEntity<byte[]> request = new HttpEntity<byte[]>(null, getHeaders(true, "GET", null, canonicalPath, sessionId));
//getHeaders() will return HttpHeaders with those parameter
ResponseEntity<byte[]> response = null;
try {
restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
response = this.restTemplate.exchange(uri, HttpMethod.GET, request, byte[].class).getBody();
return response;
} catch( HttpServerErrorException hse ){
throw hse;
}
return null;
}
Note about HttpMessageConverter, instead using list, i can directly add a ByteArrayHttpMessageConverter()
As said I guess you must use the right messageconverter
I would do in this way:
private RestTemplate restTemplate;
public byte[] getProfilePic(){
String canonicalPath = "http://dockertest/bankingapp/customer/profpicFile";
String sessionId= "MTQ4NzE5Mz...etc";
List<HttpMessageConverter> converters = new ArrayList<>(1);
converters.add(new ByteArrayHttpMessageConverter());
restTemplate.setMessageConverters(converters);
HttpEntity<byte[]> request = new HttpEntity<byte[]>(null, getHeaders(true, "GET", null, canonicalPath, sessionId));
//getHeaders() will return HttpHeaders with those parameter
ResponseEntity<byte[]> response = null;
try {
response = this.restTemplate.exchange(uri, HttpMethod.GET, request, byte[].class);
} catch( HttpServerErrorException hse ){
throw hse;
}
return response;
}
More information can be found here: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#setMessageConverters-java.util.List- and here https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/HttpMessageConverter.html and here https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/converter/ByteArrayHttpMessageConverter.html
Thank you very much,this problem takes up my a lot of time. Now,it was resolved.
following:
#Configuration
#Slf4j
public class RestTemplateConfiguration implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
RestTemplate restTemplate = (RestTemplate) applicationContext.getBean("restTemplate");
restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
restTemplate.setUriTemplateHandler(new GetUriTemplateHandler());
}
}
I have a problem.
I don't know how to return a view in a method with a return type of ResponseEntity.
I want to download a file with my controller.
The download works fine if a file was uploaded.
If no file were uploaded, it just should do nothing (return the actual view).
Now I´m not sure how to do this because I guess it's not possible returning a view (For that I needed return-type String).
Do you have any idea?
#Controller
public class FileDownloadController {
#RequestMapping(value="/download", method = RequestMethod.GET)
public ResponseEntity fileDownload (#Valid DownloadForm form, BindingResult result) throws IOException{
RestTemplate template = new RestTemplate();
template.getMessageConverters().add(new FormHttpMessageConverter());
HttpEntity<String> entity = new HttpEntity<String>(createHttpHeaders("test.jpg", "image/jpeg"));
UrlResource url = new UrlResource("www.thisismyurl.com/images" + form.getImageId());
return new ResponseEntity<>(new InputStreamResource(url.getInputStream()), createHttpHeaders("test.jpg", "image/jpeg"), HttpStatus.OK);
}
private HttpHeaders createHttpHeaders(String filename, String contentType) {
HttpHeaders headers = new HttpHeaders();
headers.setAll(getHttpHeaderMap(filename, contentType));
return headers;
}
private Map<String,String> getHttpHeaderMap(String filename, String contentType) {
Map<String, String> headers = new HashMap<>();
headers.put("Content-disposition", "attachment; filename=\"" + filename + "\"");
headers.put("Content-Type", contentType);
return headers;
}
}
Hi i had a similar problem in my project once, i.e., I have to different return types view vs string based on some logic.
First it’s definitely not possible to return a model and view when you have response entity as return type.
I solved this using generic return type
public <T> T fileDownload (#Valid DownloadForm form, BindingResult result) throws IOException{
//your code
//here you can return response entity or
//modelAndView based on your logic
}
I've found that this works with Spring Boot 2 and JSP views:
#GetMapping(value = "/view/theobject/{id}")
public Object getDomainObject(ModelAndView mav, #PathVariable Long id) {
Optional<DomainObject> theObject = svc.getDomainObject(id);
if (theObject.isPresent()) {
mav.setViewName("viewdomainobject");
mav.addObject("theObject", theObject.get());
return mav;
}
return ResponseEntity.notFound().build();
}
There's no need for the unpleasant <T> T generic return type, or casting the returned object.
I encountered a problem with downloading grid fs stored image via spring controller. When trying to open the downloaded file the image viewer says that it is corrupted, it turns out that the image is in base64 format.
There is the controller part:
#Override
#RequestMapping(value = "/image_download", method = RequestMethod.GET)
public ResponseEntity<byte[]> downloadImage(...) throws IOException {
final GridFSDBFile image = getImageFromGrifFs(...);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.valueOf(image.getContentType()));
headers.setContentDispositionFormData("attachment", image.getFileName());
final byte[] content = IOUtils.toByteArray(image.getInputStream());
return new ResponseEntity<>(content, headers, HttpStatus.OK);
}
Spring version is 4.3.11.
And here are the message converters:
#Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter());
converters.add(byteArrayHttpMessageConverter());
super.configureMessageConverters(converters);
}
#Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
final ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
return arrayHttpMessageConverter;
}
private List<MediaType> getSupportedMediaTypes() {
final List<MediaType> list = new ArrayList<>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.IMAGE_PNG);
list.add(MediaType.IMAGE_GIF);
list.add(MediaType.APPLICATION_OCTET_STREAM);
return list;
}
I also tried using InputStreamResource the following way in the controller:
return ResponseEntity.ok()
.contentLength(image.getLength())
.contentType(MediaType.parseMediaType(image.getContentType()))
.body(new InputStreamResource(image.getInputStream()));
But got the exception:
Could not write content: No serializer found for class com.mongodb.gridfs.GridFSDBFile$MyInputStream
Any help appreciated. Thank you.
After I'd done some more digging I found good explanation: https://stackoverflow.com/a/44943494/2421204
And indeed adding (produces = "image/jpeg") to RequestMapping solved the issue.
#RequestMapping(value = "/image_download", method = RequestMethod.GET, produces = "image/jpeg")
The images that are downloaded are in binary.