Problem With outputStream after 1 download - spring

After downloading an Excel with HttpServletResponse the second time i tried to download it dosent work.
public void DatosaExcel(HttpServletResponse response) throws IOException {
writeHeaderLine();
writeDataLines();
ServletOutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
outputStream.close();
}
#GetMapping("/DescargarExcelEmpleados")
public void DescargarEmpleados(HttpServletResponse response) throws IOException {
response.setContentType("application/octet-stream");
String headerKey = "Content-Disposition";
String headerValue = "attachment; filename=empleados.xlsx";
response.setHeader(headerKey, headerValue);
productService.DatosaExcel(response);
}
those are my Ctrl and function that calls the excel generator but i cant find a way of solving this issue

I'm guessing you are using Apache POI for Excel work here and also guess that nothing happens when you try to generate it the second time (if there is an error, please provide the log).
This is probably because you didn't close the workbook after write. Always
workbook.write(outputStream);
workbook.close(); //<-- Important!!!

I found the answer i have the workbook declaration XSSFWorkbook workbook = new XSSFWorkbook();, on the service class, so after 1 download the object was already full. so a work aroundi used was in the controller function call i initialized the object again.
workbook = new XSSFWorkbook();

Related

java.lang.IllegalStateException: getWriter() has already been called for this response, Even thought its called only once

I am using spring-boot. I want to send a CSV as the attachment of response for which I am using opencsv to write bean to response. Even though response.getWriter() is called only once, I am getting this exception.
While searching for solution, I came to know that we cannot use response.getWriter() and response.getOutputStream() together. But this is not the case here.
I am only calling getWriter and exactly once.
I also checked if it was due to opencsv library by writing a plain string to it e.g. "name, test". But still the same error. So it's not due to opencsv either.
private Pair<Boolean, String> writeCSVToResponse(List<QuestionDownloadResponse> qdrList, HttpServletResponse response) {
String fileName = new SimpleDateFormat("'CSV'yyyyMMddHHmmss'.csv'").format(new Date());
response.reset();
response.setContentType("application/csv");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + fileName + "\"");
response.setCharacterEncoding("UTF-8");
//Find answer, for now we will return any if exists
try {
Writer writer = response.getWriter();
// Create Mapping Strategy to arrange the
// column name in order
final CustomCSVMappingStrategy<QuestionDownloadResponse> mappingStrategy = new CustomCSVMappingStrategy<>();
mappingStrategy.setType(QuestionDownloadResponse.class);
StatefulBeanToCsv<QuestionDownloadResponse> sbc = new StatefulBeanToCsvBuilder<QuestionDownloadResponse>(writer)
.withSeparator(CSVWriter.DEFAULT_SEPARATOR)
.withMappingStrategy(mappingStrategy)
.build();
sbc.write(qdrList);
} catch (IOException e) {
CMSQuestionServiceImpl.logger.error("Error in CSV IO operations", e);
return Pair.of(false, "Failed to Open file");
} catch (CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
CMSQuestionServiceImpl.logger.error("Error in CSV Write operation", e);
return Pair.of(false,"Failed to write in csv");
}
}
Why is this exception coming, even when response.getWriter is called exactly once. How to solve this issue.
I found the issue. The problem was that I was sending a String (a message) in response in the controller, but in service, I am sending an attachment. So getWriter() was being called first time in service by me, and the second time by spring to write the response message of type String. Solution: I set the return type of method in the controller to void ex::
#GetMapping(value = "/downloadCSV")
public void downloadCSV(#RequestBody(required = false) List<Integer> items, HttpServletResponse response){
The only problem is if something goes wrong in service, you cannot send an error message and there are other alternatives to overcome it.
I had the same error: in spring-boot
This error occures when we use a RestExceptionHandler which tries to modify the response by using response.getOutputStream(), remember, the response.getWriter() is already called (even once in our code).
So when we call response.getWriter() , and we have some exception, the RestExceptionHandler executes the response.getOutputStream() which causes java.lang.IllegalStateException: getWriter() has already been called for this response.
What I suggest to do is to add try-catch to the response.getWriter() and when you catch any exception we have to reset the response by using response.reset() like that:
try {
writer.write(beans);
} catch (Exception e) {
response.reset();
throw e;
}
After throwing the exception the RestExceptionHandler will do it's job.

Media Type not acceptable exception when using SseEmitter

Following this tutorial, I am trying to set up a Sse Emitter. When I open the html page I get a
Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
On client side (javascript) it sais it cannot connect to the server. I have tried various other tutorials, but I am clueless on why my code isnt working.
I set up a clean test project containing only and exactly the tutorial code.
I Was in the middle of doing something else when I got the same issue.
The code below fixed it.
Simply put Mismatch Media type.
#GetMapping(value = "/api/push/notification",headers = "Accept=*/*", consumes = MediaType.ALL_VALUE, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public String doNotify(#RequestParam("authToken") String token, #RequestParam("clientId") String clientId, HttpServletResponse response) throws InterruptedException, IOException {
response.addHeader("charset","UTF-8");
final SseEmitter emitter = new SseEmitter(30000l);
service.addEmitter(clientId,emitter);
service.sendConnectedNotification(clientId);
emitter.onCompletion(() -> service.removeEmitter(clientId));
emitter.onTimeout(() -> service.removeEmitter(clientId));
return "Connected OK";
}
any my event handler
#Async
public void doNotify(String clientId, Object data) {
SseEmitter emitter= emitters.get(clientId);
if(emitter!=null) {
try {
emitter .send(SseEmitter.event() .reconnectTime(30000)
.data(data,MediaType.APPLICATION_JSON)
.id(UUID.randomUUID().toString())
.name("Notification")
.comment("Client connection notification")
);
} catch (Exception e) {
emitters.remove(clientId);
}
}
}

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

spring boot HttpServletResponse not setting file name

I have a rest service like this:
import org.apache.tomcat.util.http.fileupload.IOUtils;
#RequestMapping(value = "/xxx", method = GET)
public void getExcel(HttpServletResponse resp) {
resp.setHeader("Content-Disposition", "attachment; filename=\"NAME.xlsx\"");
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
try (ServletOutputStream outputStream = resp.getOutputStream()) {
IOUtils.copy(A-VALID-FILE-INPUT-STREAM, outputStream);
resp.flushBuffer();
} catch (IOException e) {
throw new AppException(e);
}
}
the problem is that every time I call this service the default save name is 'response', I have tried returning HttpEntity<byte[]>, create objects like HttpHeaders() but nothing changes.
Any help is appreciated
If you are using postman take a look at https://github.com/postmanlabs/postman-app-support/issues/2082
Seems that you will need to wait until this issue will be addressed by postman team.

Couldn't find HttpResponse or HttpServletResponse in Spring 4

Just I am working on web project, In that i need to download a sql file while clicking the link. I tried to find the HttpResponse or HttpServletResponse in controller. Could any one help me to resolve this issue,
#RequestMapping(value = "/downloadFile.htm", method = RequestMethod.GET)
public void toDownloadFile(#RequestParam("fileName") String fileName,
HttpServletResponse response) {
File file = new File(fileName);
if (file != null) {
try {
response.setContentType("application/sql");
// response.setContentLength((new
// Long(file.getLength()).intValue()));
response.setHeader("content-Disposition",
"attachment; filename=" + fileName);
FileCopyUtils.copy(fileName, response.getOutputStream());
} catch (IOException ex) {
LOGGER.error("Exception in toDownloadFile :" + ex);
}
}
}
But in Spring 3 its available, I hope they removed or renamed the HttpServletResponse in Spring 4. Because HttpServeltRequest has been moved to org.springframework.web.context.request.WebRequest. Any one looked into this? Thanks in advance!!!
HttpServeltRequest and HttpServletResponse are javax interfaces not spring.
Are your project dependencies set up correctly?
javax.servlet.http.HttpServletResponse
org.springframework.web.context.request.WebRequest has been around for a while and is documented as...
Generic interface for a web request. Mainly intended for generic web request interceptors, giving them access to general request metadata, not for actual handling of the request.

Resources