Spring returning image as ResponseEntity<byte[]> - image corrupt - spring

I am working on a spring 3.2.7 app and it sends signatures stored in the database as base64 string back to the users browser via a spring controller which outputs the byte array ResponseEntity.
The image is always corrupted and I havent worked on this part of the system as I double checked in svn and the controller has not been touched since the branch I am working on was created.
I am able to convert the base64 string to an image on my desktop and I am also able to the convert the byte array returned to browser into an image before spring steps in.
Below is my code, this was apparently working before so perhaps there is some config change that could cause this?
#RequestMapping(value = "/submissions/signature/{type}/{id}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<byte[]> getSignature(#PathVariable String type, #PathVariable Integer id) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String base64 = ... gets from db
byte[] bytes = Base64.decodeBase64(base64);
BufferedImage bi = ImageIO.read(new ByteArrayInputStream(bytes));
ImageIO.write(bi, "png", baos);
HttpHeaders headers = new HttpHeaders();
headers.setLastModified(Calendar.getInstance().getTime().getTime());
headers.setCacheControl("no-cache");
headers.setContentType(MediaType.IMAGE_PNG);
headers.setContentLength(baos.toByteArray().length);
//Image as base64 string is ok in converter
System.out.println("BASE 64 IMAGE IS: " + base64);
//This image is created ok on desktop
FileOutputStream fos = new FileOutputStream("C:\\Users\\p\\Desktop\\test_signature.png");
fos.write(bytes);
fos.close();
//This image is created ok on desktop
FileOutputStream fos3 = new FileOutputStream("C:\\Users\\p\\Desktop\\test_signature_baos.png");
fos3.write(bytes);
fos3.close();
return new ResponseEntity<byte[]>(baos.toByteArray(), headers, HttpStatus.OK);
}
The image is being rendered in the browser like:
<img id="userSignature" width="296" height="110" style="border:0px" src="/webapp/service/submissions/signature/user/${subid}" alt="User signature" />
I have not changed this class and I am told that it did work, I am able to create images from both byte arrays and they are ok and looks the same and I able to render the signature string ok for testing like:
<IMG SRC="data:image/png;base64, <base_64_string>" ALT="">
has anyone experienced similar issues or know what could be causing this?
I have now tried sending an image from my file system already created as png and that also fails.
I have now noticed that CSV files do not download properly in the app and they stream in the same way:
#RequestMapping(value = "/results/csv", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<byte[]> getResultsInCsvFormat() throws IOException {

I have successfully returned file contents with the help of an InputStream:
#RequestMapping(value = "/submissions/signature/{type}/{id}",
method = RequestMethod.GET)
public HttpEntity getFile(HttpServletResponse response,
#PathVariable String type,
#PathVariable Integer id) {
String base64 = "foo"; // get base-64 encoded string from db
byte[] bytes = Base64.decodeBase64(base64);
try (InputStream inputStream = new ByteArrayInputStream(bytes)) {
StreamUtils.copy(inputStream, response.getOutputStream());
response.setContentType(MediaType.IMAGE_PNG_VALUE);
} catch (IOException e) {
// handle
}
return new ResponseEntity(HttpStatus.OK);
}
Note that I'm not using ResponseBody, and in my working version I am using MediaType.APPLICATION_OCTET_STREAM_VALUE rather than the actual file content type.

The accepted solution didn't work for me in Spring Boot 2.x for Base64 images. Here is how I returned a Base64 image:
#GetMapping(value = "/pixels/{id}", produces = MediaType.IMAGE_PNG_VALUE)
#ResponseBody
public byte[] pixelTracking(#PathVariable String id) {
// TODO: do whatever you want here
// return png image
String base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";
return Base64.getDecoder().decode(base64);
}

Ok so I have fixed this now with some thanks to beerbajay who showed me that downloading by streaming to the response directly was ok and also that I should look at ByteArrayHttpMessageConverter.
It turned out that I had made a mistake in the spring config and I realised this after reading the spring documentation that told me that ByteArrayHttpMessageConverter is registered automatically when using <mvc:annotation-driven />.
The mvc annotation driven tag had been removed from the config as I thought this was doing the same thing (and I thought it only needed to be declared once within a spring context):
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
It looks like this alone isnt sufficient to set up annotation-driven in the app and I have added back in the tag to the context.xml file.
I dont understand why this needs to be in two places though as all the xml config is loaded through same xml file in what I assumed was the same spring context:
<import resource="config/properties.xml" />
<import resource="config/security.xml" />
<import resource="config/context.xml" />
<import resource="config/content-negotiation.xml" />
<import resource="config/rest-client.xml" />

my solution is :
backend is spring boot and frontend is reactjs
bug is different syntax is backend and frontend : java use ("_","-") and web(reactjs,...) use ("/","+") |
for example : "PGjQOA66-_ne-" convert to "PGjQOA66+//ne/+"
you can testing base64 in this link :https://onlinepngtools.com/convert-base64-to-png
back end code :
ByteArrayOutputStream baos = new ByteArrayOutputStream();
File file = new File(path);/// path : is external directory and local directory in server
FileInputStream imageInFile = new FileInputStream(path);
byte imageData[] = new byte[(int) file.length()];
imageInFile.read(imageData);
String base64 = encodeImage(imageData);
/// 1. Not show if size up 300KB !!! :|
/// 2. base64.replaceAll("_","/").replaceAll("-","+")
byte[] bytes = Base64.decodeBase64(base64.replaceAll("_","/").replaceAll("-","+"));
BufferedImage bi = ImageIO.read(new ByteArrayInputStream(bytes));
ImageIO.write(bi, "png", baos);
HttpHeaders headers = new HttpHeaders();
headers.setLastModified(Calendar.getInstance().getTime().getTime());
headers.setCacheControl("no-cache");
headers.setContentType(MediaType.IMAGE_PNG);
headers.setContentLength(baos.toByteArray().length);
return new ResponseEntity<byte[]>(baos.toByteArray(), headers, HttpStatus.OK);
and other solution for back end:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
File file = new File(path);/// path : is external directory file and local directory file in server, or path get of database.
FileInputStream imageInFile = new FileInputStream(path);
byte imageData[] = new byte[(int) file.length()];
imageInFile.read(imageData);
/// 1. Not show if size up 300KB !!! :| i dont now!
/// 2. base64.replaceAll("_","/").replaceAll("-","+")
String base64 = encodeImage(imageData).replaceAll("_","/").replaceAll("-","+");
return base64;
and reactjs code is :
const [imageData, setImageData] = React.useState({});
setImageData(...request to backend);
<img src={`data:image/png;base64,${imageData}`} alt="This Is alert" />

Related

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();
}

Encoding for downloaded files in Spring

I want to create a controller which will sent to client a CSV file, and I created the next controller:
#ResponseStatus(value = HttpStatus.OK)
#RequestMapping(value = "/csv", method = RequestMethod.GET)
public ResponseEntity downloadCsvAllInvoiceTransactionsByFilter(
#PageableDefault(direction = DESC, sort = "invoiceDate", size = 30) Pageable pageRequest) throws IOException {
String someVariable = "Some text";
byte[] out = someVariable.getBytes(Charset.forName("UTF-8"));
HttpHeaders responseHeaders = new HttpHeaders();
LOGGER.info(new String(out));
responseHeaders.add("content-disposition", "attachment; filename=transactions.csv" );
responseHeaders.add("Content-Type","text/csv; charset=utf-8");
return new ResponseEntity<>(out,responseHeaders,HttpStatus.OK);
}
Logger is displaying the correct string:
Some text
but in downloaded file there is another one
U29tZSB0ZXh0
How can I fix this?
Body of ResponseEntity goes through a message converter before it gets sent. The choice of the particular converter depends on class of the body and response and request headers.
I tried to reproduce the issue with your code snippet and got expected text in csv file. So I assume that you got a message converter registered that converts byte arrays the way you observe.
You can debug AbstractMessageConverterMethodProcessor#writeWithMessageConverters and see which converter is chosen and why.

SPRING MVC 3 - NOT displaying image in JSP

I have a controller serving images from an external directory (say c:\images\userID\photo.png), and this controller does its job very well. However, the img tag in my JSP file shows the image icon instead of the image returned by this controller.
Here's my controller:
#RequestMapping(value = "/load/{imageId}/", method = RequestMethod.GET)
public ResponseEntity<byte[]> loadImage(#PathVariable("imageId") Long imageId, HttpServletRequest request)
{
final org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
BufferedImage image;
Photo photo = photoManager.getSinglePhoto(imageId);
headers.setContentType(MediaType.IMAGE_PNG);
try
{
if (photo == null)
{
File defaultFile = new File("c:/images/default.png");
image = ImageIO.read(defaultFile);
return new ResponseEntity<byte[]>(((DataBufferByte)image.getData().getDataBuffer()).getData(), headers, HttpStatus.CREATED);
}
File file = new File(photo.getPath());
image = ImageIO.read(file);
return new ResponseEntity<byte[]>(((DataBufferByte)image.getData().getDataBuffer()).getData(), headers, HttpStatus.CREATED);
}
catch (IOException ex)
{
return new ResponseEntity<byte[]>(null, headers, HttpStatus.NOT_FOUND);
}
}
I have found reading other answers here, that I need to include messageConverters in my application context, and I did it.
Here's a portion of my application-context.xml
<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>
The eclipse XML editor complains about MethodHandlerAdapter being deprecated.
JSP:
<img src="/mavenspringapp/photo/load/131/" width="128" height="128" alt="laf02.jpg">
Why isn't the image getting displayed even when the controller sends the response correctly (201). Thanks in advance.
A little bit of googling tells me that HTTP 201 means created.
If the image exists, why are you sending a response code telling the client you just created the image?
I don't know for sure how web browsers handle it, but perhaps try changing your response codes to 200, as you aren't really creating anything.
The problem was in the controller method. Apparently loading the image the way I did it, is not correctly done. So I modified my method as shown below:
#RequestMapping(value = "/load/{imageId}/", method = RequestMethod.GET)
public ResponseEntity<byte[]> loadImage(#PathVariable("imageId") Long imageId, HttpServletRequest request)
{
final org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
Photo photo = photoManager.getSinglePhoto(imageId);
headers.setContentType(MediaType.IMAGE_PNG);
try
{
if (photo == null)
{
File defaultFile = new File("c:/images/default.png");
byte[] content = FileUtils.readFileToByteArray(defaultFile);
return new ResponseEntity<byte[]>(content, headers, HttpStatus.OK);
}
File file = new File(photo.getPath());
byte[] content = FileUtils.readFileToByteArray(file);
return new ResponseEntity<byte[]>(content, headers, HttpStatus.OK);
}
catch (IOException ex)
{
return new ResponseEntity<byte[]>(null, headers, HttpStatus.NOT_FOUND);
}
}
And now it works great! I hope this helps somebody else. Thank you all for your replies.
You may consider the alternative instead of controller displaying the images you can directly access images your jsp for this you need to put the the mapping information in your spring configuration xml like
<mvc:resources mapping="/image/**" location="file:///D:/images/" /> and in your jsp file you can directly can directly call
<img src="<spring:url value='/images/logo.png'/>" /> and make sure your have mentioned the spring tag in your jsp
<%#taglib uri="http://www.springframework.org/tags" prefix="spring"%>

Not able to read Multipart file using spring controller when file passed via apache httpclient

On my UI page, I am trying to upload one file by setting the enctype="multipart/form-data" and encoding="multipart/form-data" on my html form.
Able to read the file contents successfully in my server class (servlet) using the org.apache.commons.fileupload.servlet.ServletFileUpload APIs.
After that I am trying to pass the file or its contents to another server using the apache commons Httpclient using its muiltpart option, I can verify the contents are being passed to another layer (by checking the request body contents or verifying the request in chrome developer tool)
But on another server layer (which is spring based controller),when trying to read the uploaded file using to be uploaded via but not getting the contents. Rather it says "Required MultipartFile parameter 'fileContents' is not present".
Could you please help me out what could be the possible issue for not getting file in Spring controller.
Server Class / servlet Implementation for posting the file to different server:
HttpMethod httpMethod = new PostMethod(epsURL);
String contentTypeRequested = request.getContentType();
httpMethod.setRequestHeader("Content-type", contentTypeRequested);
if(isMultipart){
String content = getUploadFileContents(request);
File file = null;
try {
file = new File("fileContents");
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw;
fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content.toString());
bw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
try{
Part[] parts = {
new FilePart(file.getName(), file)
};
MultipartRequestEntity multipart = new MultipartRequestEntity(parts, httpMethod.getParams());
((PostMethod) httpMethod).setRequestEntity(multipart);
}catch(Exception e){
e.printStackTrace();
}
}
2.Spring layer changes inside context-config.xml:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="100000"/>
<property name="maxInMemorySize" value="100000"/>
</bean>
3.Spring Controller Implementation:
#RequestMapping(method = RequestMethod.POST, value = "/type/details")
public
void getTypeDetails(
#RequestParam("fileContents") MultipartFile file,
HttpServletRequest httpRequest) {
/// some business logic here based on file object.
}
I am getting the below error:
Error:
2014-04-16 16:28:51,638 [http-bio-8080-exec-2] ERROR com.MyControllerImpl - Exception Occured: : org.springframework.web.bind.MissingServletRequestParameterException: Required MultipartFile parameter 'fileContents' is not present
at org.springframework.web.method.annotation.RequestParamMethodArgumentResolver.handleMissingValue(RequestParamMethodArgumentResolver.java:208)
I find out that if you want to get Multipart file in your controller without its name, you can try something like this:
#RequestMapping(method = RequestMethod.POST, value = "/type/details")
public
void getTypeDetails( any other parameters,
MultipartRequest request) {
/// some business logic here based on file object.
}
The MiltipartRequest will contain all information about multipart request section.

Spring MVC: how to download Excel from DB

I have a problem with Spring MVC and Excel. I store excel file in database as lob. Like this part of my entity database is PostgreSQL
#Lob
#Column(name = "Exel")
private String exel;
Next I would like to get it from DB and user can download it from web page this is the controller
#RequestMapping(value = "/downloadExelTemplate.xls", method = RequestMethod.GET)
public void downloadExelTemplate(HttpServletResponse response)
throws IOException {
response.setContentType("application/x-msexcel");
ExelDTO exel = service.getExel(new Long(1));
InputStream is = new ByteArrayInputStream(exel.getExel().getBytes());
BufferedWriter outex = new BufferedWriter(new FileWriter("out.xls"));
outex.write(exel.getExel());
outex.close();
ServletOutputStream out = response.getOutputStream();
out.write(exel.getExel().getBytes());
is.close();
out.close();
}
And I get not proper xls file with the bugs.
Please help me. What is wrong? When I get file from stream is the same effect.
So I resolve my problem, I change the type of data in hibernate entity to
#Lob
byte[] exelData;
and everything work well. When there was only text file there was no problem to mapping the #lob to the string but connected with ms office formats it fails. So I learned how to keep data file correctly.

Resources