Httpcache on springboot - spring-boot

I have springboot application that requests an image from url then display it on browser. I want to cache my response using cache-control header.
I use ResponseEntity and already set my header with eTag. I already checked response header in my browser and it shows :
Cache-Control:"max-age=31536000, public"
Content-Type:"image/jpeg;charset=UTF-8"
Etag:"db577053a18fa88f62293fbf1bd4b1ee"
my request also have If-None-Match header. But, I always get 200 status instead of 304.
Here's my code
#RequestMapping(value = "/getimage", method=RequestMethod.GET)
public ResponseEntity<byte[]> getImage() throws Exception {
String url = "www.example.com/image.jpeg";
String eTag = getEtag(url);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("image", "jpeg"));
headers.add("Cache-Control", "max-age=31536000, public");
headers.add("ETag", eTag);
URL imageUrl = new URL(url);
InputStream is = imageUrl.openStream();
BufferedImage imBuff = ImageIO.read(is);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(imBuff, "jpeg", baos);
byte[] image = baos.toByteArray();
return new ResponseEntity<byte[]>(image, headers, HttpStatus.OK);
}
Can anyone help me?
UPDATE
I tried using method described in Unable to cache images served by Spring MVC so my code become :
#RequestMapping(value = "/getimage", method=RequestMethod.GET)
public ResponseEntity<byte[]> getImage() throws Exception {
String url = "www.example.com/image.jpeg";
String eTag = getEtag(url);
URL imageUrl = new URL(url);
HttpURLConnection httpCon = (HttpURLConnection)imageUrl.openConnection();
long lastModified = httpCon.getLastModified();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("image", "jpeg"));
headers.add("Cache-Control", "max-age=31536000, public");
headers.add("ETag", eTag);
headers.add("Last-Modified", new Date(lastModified).toString());
if (webRequest.checkNotModified(eTag)) {
return null;
}
InputStream is = imageUrl.openStream();
BufferedImage imBuff = ImageIO.read(is);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(imBuff, "jpeg", baos);
byte[] image = baos.toByteArray();
return new ResponseEntity<byte[]>(image, headers, HttpStatus.OK);
}
But now i always get 304 status code even if i change the url. I checked webRequest.checkIsNotModified(...) either by eTag and last-modified and it always return true. Did I do something wrong here?

I ended up changing my code. Instead of using webRequest.checkNotModified(eTag) i manually check last-modified and eTag from my resource (which is from s3) and compare it to if-modified-since and if-none-match in request header.
Besides, i also move everything related to http caching in a filter.

Related

When trying to invoke rest API throws httpmediatypenotsupportedexception content type 'application/x-www-form-urlencoded' not supported

I am trying to invoke rest API below which consumes a multi-part file. First paramter is a MultipartFile and second is s String. and this functions processes some business logic
#PostMapping( value="/uploadFile", consumes = MediaType.MULTIPART_FORM_DATE_VALUE)
public ResponseEntity<String> upload(#RequestPart("file") MultipartFile file,
#RequestParam("path") String path){
//businness logic
}
Invoking above API from code below. But it throws
httpmediatypenotsupportedexception content type
'application/x-www-form-urlencoded' not supported. I have also tried
added header "Content-type", MediaType.MULTIPART_FORM_DATA_VALUE OR
"Content-Type", "multipart/form-data" OR "Accept",
"multipart/form-data" in the headers below, but that has not helped
either
public void uploadFile() {
Path path = Paths.get("C:/ABC.txt");
byte[] content = null;
try{
content = Files.readAllBytes(path); // All file is read in content variable
} catch(final IOException e){
}
MultipartFile file = new MockMultipartFile("ABC.txt",content);
UriComponentsBuilder urlBuilder = UriComponentsBuilder.fromHttpUrl(oauthURL);
urlBuilder.queryParam("file", file);
urlBuilder.queryParam("path", "/temp);
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> response = null;
HttpEntity<?> entity = new HttpEntity<>(headers);
try{
response = restTemplate.exchange(urlBuilder.build().encode().toUri(), HttpMethod.POST, entity. String.class);
}
catch (Exception e){
}
}
}
Your server accepts (consumes) "multipart/form-data" however you are sending the file and path in the URL. This will always result in a "application/x-www-form-urlencoded".
So you need to change the server to accept it as you send them or send the file and path as the body (within the entity)
EDIT (some code to show how):
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("file", file);
body.add("path","/temp");
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
response = restTemplate.postForEntity(serverUrl, requestEntity, String.class);

How to set the produces value dynamically in spring rest controller?

I need to implement API which can either send response or download a file:
#GetMapping(value = "/download")
public ResponseEntity<?> downloadFile(
#RequestParam(value = "apiResponseType", required = true) String apiResponseType) throws IOException {
ValidationResponse response = null;
if (apiResponseType.equals("FILE")) {
String FILE_HEADER = "id,firstName,lastName,gender,age";
byte[] json = FILE_HEADER.getBytes();
Resource resource = new ByteArrayResource(json);
HttpHeaders headers = new HttpHeaders();
headers.setContentLength(resource.contentLength());
headers.setContentDispositionFormData("attachment", "test.csv");
return ResponseEntity.ok().headers(headers).contentType(MediaType.APPLICATION_OCTET_STREAM).body(resource);
} else {
response = new ValidationResponse();
response.setSuccess(true);
response.setMessage("TESTING");
return ResponseEntity.ok(response);
}
}
Above code is working for "ELSE" case. i.e., can able to send response.
But if I add "produces" to #GetMapping like below, I am able to download the file but not working for response (else case in above code) (Got status: 406 Not Acceptable):
#GetMapping(value = "/downloadFile", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
Can anyone help me with this ?
Did you try return ResponseEntity.ok(response).contentType(MediaType.TEXT_PLAIN); on the else branch (and remove the produces)?
Your question has invalid assumption 'which can either send response or download a file'. Download file is nothing else that sending response! For API client it is no difference. For browser it is just implementation details if browser proposes to save the response as file or shows response in browser window.
Setting the MediaType via a call to contentType on the ResponeEntitiy no longer works. By now (SpringBoot 2.7.1) you have to set the MediaType to the headers.
I wrote this method to dynamically create headers with a given MediaType provided as a string.
final HttpHeaders httpHeaders = new HttpHeaders();
final MediaType mediaType;
switch (responseType) {
case "json":
mediaType = MediaType.APPLICATION_JSON;
break;
case "plain":
case "text":
mediaType = MediaType.TEXT_PLAIN;
break;
default:
final var parts = responseType.split("/");
if (parts.length < 2)
throw new IllegalArgumentException(String.format("Unrecognizable MediaType '%s'", responseType));
mediaType = new MediaType(parts[0], parts[1]);
break;
}
LOGGER.debug("Using mediaType {}", mediaType);
httpHeaders.setContentType(mediaType);

Spring response entity image

I wrote a rest controller to return an image associated with a primary key. Now I wanted to load this image in the browser and I am running into issues:
(1) If I type a GET URL to the image the browser (FireFox and Chrome) don't display the image but they are seeing all the headers properly. Additionally firefox says "The image cannot be displayed because it contains errors"
(2) If I used XMLHttpRequest to create get the image using the URL I get the image but it displays only partially (the bottom half is cut off and is set to transparent).
#GetMapping("/{featureId}/loadImage")
public ResponseEntity<byte []> loadImageForId(#PathVariable long featureId, HttpServletResponse response) throws IOException {
log.info("Getting image for feature id " + featureId);
Feature feature = featureService.getFeatureById(featureId);
File file = featureService.loadImageForFeature(feature);
byte [] imageData = new byte[(int) file.length()];
FileInputStream inputStream = new FileInputStream(file);
inputStream.read(imageData);
inputStream.close();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(...));
headers.setContentLength(file.length());
response.setHeader("Content-Disposition", "inline; filename=" + file.getName());
return new ResponseEntity<byte[]>(imageData, headers, HttpStatus.OK);
}
if it is working on tomcat, you can use this tomcat's utility class :
import org.apache.tomcat.util.http.fileupload.IOUtils
for example:
response.setContentType("image/jpeg");
InputStream is = new ByteArrayInputStream(imageByteArray);
IOUtils.copy(is,response.getOutputStream());
Okay finally after hours of debugging with curl etc, I was able to verify that the response body was not getting properly encoded image (nothing to do with the headers).
This was caused due to the choice of InputStream and OutputStream objects.
Instead of using FileInputStream I switched to using ImageIO and the underlying BufferedImage to write the output to the ServletResponse as follows:
#GetMapping("/{featureId}/loadImage")
public void loadImageForId(#PathVariable long featureId, HttpServletResponse response) throws IOException {
log.info("Getting image for feature id " + featureId);
Feature feature = featureService.getFeatureById(featureId);
File imageFile = featureService.loadImageForFeature(feature);
MediaType mediaType = MediaType.parseMediaType(Files.probeContentType(imageFile.toPath()));
response.setHeader("Content-Disposition", "inline; filename=" + imageFile.getName());
response.setStatus(HttpStatus.OK.value());
response.setContentType(mediaType.toString());
response.setContentLength((int)imageFile.length());
OutputStream os = response.getOutputStream();
ImageIO.write(ImageIO.read(imageFile), mediaType.getSubtype(), os);
os.flush();
os.close();
}

How can I fix this issue, when file download in the browser, it change encoding in file, I used Spring boot

I used it code for response Excel byte[] to browser. But I have problem, because Spring boot encode file, and I got bad file than download from browser.
//This method returned response on controller
public ResponseEntity<ByteArrayResource>
returnAllTransactionAsExcel(TransactionSearchFilter
transactionSearchFilter) throws IOException {
List<Transaction> transactions =
getAllTransactions(transactionSearchFilter);
byte[] ourFile=writeIntoExcel(transactions);
//headers
HttpHeaders headers = new HttpHeaders();
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
headers.add("Content-Disposition", "attachment;
filename=list_transactions.xls");
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
headers.setContentType(MediaType.parseMediaType("application/vnd.ms-excel"));
return
ResponseEntity
.ok()
.headers(headers)
.contentLength(ourFile.length)
.contentType(MediaType.parseMediaType("application/vnd.ms-excel"))
.body(new ByteArrayResource(ourFile));
}
//This method on controller
#ApiOperation(value = "Retrieve all transactions in Excel")
#RequestMapping(value = "/allExcel", method = RequestMethod.POST,
produces "application/vnd.ms-excel")
public ResponseEntity<ByteArrayResource>
getAllTransactionsAsExcel(#RequestBody TransactionSearchFilter
transactionSearchFilter) throws IOException {
return returnAllTransactionAsExcel(transactionSearchFilter);
}
I solved my problem, all problem was in swagger-ui
https://github.com/webanno/webanno/issues/459

Spring MVC Image response content type set text/html

I have simple controller method to return image like
#RequestMapping(value = "{id}", method = RequestMethod.GET)
public ResponseEntity<InputStreamResource> get(#PathVariable Long id, #RequestParam Map<String, String> params) {
ResponseEntity<InputStreamResource> response = null;
MyImage image = getImage(id);
HttpHeaders respHeaders = new HttpHeaders();
String contentType = image.getContentType();
if (contentType != null) {
respHeaders.setContentType(MediaType.valueOf(contentType));
}
respHeaders.setContentLength(image.getLength());
respHeaders.setContentDispositionFormData("attachment", image.getFilename());
InputStreamResource isr = new InputStreamResource(fileInfo.getInputStream());
response = new ResponseEntity<InputStreamResource>(isr, respHeaders, HttpStatus.OK);
return response;
}
It image has content-type, it will be setted and response has corrent header with correct content-type, but if content-type is not setted, client side response has content-type text/html.
I like to remove content-type header if content-type is unknown but even removing content-type header from header map does not help. Any ideas what would be wrong? Spring version is 3.2.14.
According to documentation, you can write something like:
#RequestMapping(value = "{id}", method = RequestMethod.GET,produces = "image/*")
or
#RequestMapping(value = "{id}", method = RequestMethod.GET,produces = {MediaType.IMAGE_JPEG_VALUE,MediaType.IMAGE_PNG_VALUE})
without specifying content type in ResposeEntity

Resources