write large HSSFWorkbook into ServletOutputStream - jersey

i have the below code. this works fine when i try to export a list of records to excel with small number of records (around 200 records):
#Path("/toExcel")
#GET
public Response excelCustomersReport(#QueryParam("page") Integer page, #QueryParam("start") Integer start, #QueryParam("limit") Integer limit, #QueryParam("filter") FilterWrapper filter, #QueryParam("sort") SortWrapper sort) throws Exception {
response.setContentType("application/octet-stream");
DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
String currentDateTime = dateFormatter.format(new Date());
String headerKey = "Content-Disposition";
String headerValue = "attachment; filename=report_" + currentDateTime + ".xlsx";
response.setHeader(headerKey, headerValue);
HSSFWorkbook workbook=userService.export(page,start,limit,filter,sort);
ServletOutputStream outputStream=response.getOutputStream();
workbook.write(outputStream);
workbook.close();
outputStream.close();
return ResponseHelper.ok(response);
}
the tragedy begins when the list size becomes larger (more than 200 records for ex). it gives the following error:
<An I/O error has occurred while writing a response message entity to the container output stream.
org.glassfish.jersey.server.internal.process.MappableException: java.io.IOException: CLOSED
i searched a lot and ended up with this solution:
public Response excelCustomersReport(#QueryParam("page") Integer page, #QueryParam("start") Integer start, #QueryParam("limit") Integer limit, #QueryParam("filter") FilterWrapper filter, #QueryParam("sort") SortWrapper sort) throws Exception {
response.setContentType("application/octet-stream");
DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
String currentDateTime = dateFormatter.format(new Date());
String headerKey = "Content-Disposition";
String headerValue = "attachment; filename=report_" + currentDateTime + ".xlsx";
response.setHeader(headerKey, headerValue);
HSSFWorkbook workbook=userService.export(page,start,limit,filter,sort);
response.setHeader("Content-Length", String.valueOf(workbook.getBytes().length));
InputStream is=new ByteArrayInputStream(workbook.getBytes());
ServletOutputStream sos = response.getOutputStream();
long byteRead = 0;
try {
byte[] buf = new byte[1000];
while (true) {
int r = is.read(buf);
if (r == -1)
break;
sos.write(buf, 0, r);
byteRead +=r;
if(byteRead > 1024*1024){ //flushes after 1mb
byteRead = 0;
sos.flush();
}
}
}catch (Exception e) {
e.printStackTrace();
}finally{
if(sos != null){
sos.flush();
}
try{is.close();}catch(Exception e){}
try{sos.close();}catch(Exception e){}
}
return ResponseHelper.ok(response);
}
flushing the stream was the only way most people were pointing to. now this solution does not work not only for large data but also for smaller ones(not working for both cases). it gives this error:
An I/O error has occurred while writing a response message entity to the container output stream.
org.glassfish.jersey.server.internal.process.MappableException: com.fasterxml.jackson.databind.JsonMappingException: Direct self-reference leading to cycle
i tried XSSFWorkbook instead of HSSFWorkbook . but weblogic did not even build the project and threw an error relating to meta-model.
anyway, what's wrong with this code. how can i write larger files into a stream? my apache poi version is 3.11.

as S.O link commented by Paul Samsotha says, i should have used StreamingOutput instead of ServletOutputStream. so the method should be rewritten as so:
#Path("/toExcel")
#GET
public Response excelCustomersReport(#QueryParam("page") Integer page, #QueryParam("start") Integer start, #QueryParam("limit") Integer limit, #QueryParam("filter") FilterWrapper filter, #QueryParam("sort") SortWrapper sort) throws Exception {
response.setContentType("application/octet-stream");
DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
String currentDateTime = dateFormatter.format(new Date());
String headerKey = "Content-Disposition";
String headerValue = "attachment; filename=report_" + currentDateTime + ".xlsx";
response.setHeader(headerKey, headerValue);
HSSFWorkbook workbook=userService.export(page,start,limit,filter,sort);
StreamingOutput output = new StreamingOutput() {
#Override
public void write(OutputStream out)
throws IOException, WebApplicationException {
workbook.write(out);
out.flush();
}
};
return Response.ok(output)
.build();
}

Related

Spring Boot prevent empty file generation on error

I am trying to extract Word document from JDBC resultset using Spring Boot . Everything is working fine , just that when there is some error , I am getting 500 Error along with an empty file . How do I prevent empty file from being generated.
#GetMapping(produces = "application/vnd.openxmlformats-officedocument.wordprocessingml.document", value = "export/word")
#ApiOperation(value = "Export table as word")
public void generateword(HttpServletResponse response, #RequestParam(required = true) String tableName,
#RequestParam(required = false) String search, #RequestParam(required = false) String searchColumn,
#RequestHeader(required = false, value = "x-remote-user") String username)
throws DataAccessException, IOException {
DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
String currentDateTime = dateFormatter.format(new Date());
String headerKey = "Content-Disposition";
String headerValue = "attachment; filename=" + tableName + "_" + currentDateTime + ".docx";
response.setHeader(headerKey, headerValue);
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
if (StringUtils.hasLength(search)) {
describeDatabaseRepository.generateWord(tableName, username, search, searchColumn, response);
} else {
describeDatabaseRepository.generateWord(tableName, username, response);
}
}
I fixed the issue by putting in try catch block.
try {
DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
String currentDateTime = dateFormatter.format(new Date());
String headerKey = "Content-Disposition";
String headerValue = "attachment; filename=" + tableName + "_" + currentDateTime + ".docx";
response.setHeader(headerKey, headerValue);
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
if (StringUtils.hasLength(search)) {
describeDatabaseRepository.generateWord(tableName, username, search, searchColumn, response);
} else {
describeDatabaseRepository.generateWord(tableName, username, response);
}
} catch (Exception e) {
response.reset();
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
ABCError err = new ABCError();
err.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
err.setDevMessage(e.getMessage());
err.setMessage("Something went wrong.Could not generate the file.");
response.getWriter().write(objectMapper.writeValueAsString(err));
}

how to write code for viewing s3 bucket image by spring boot api

I write code for download
#GetMapping(value= "/download/{fileName}")
public ResponseEntity<ByteArrayResource> downloadFile(#PathVariable String fileName) {
final byte[] data = amazonClient.downloadFile(fileName);
final ByteArrayResource resource = new ByteArrayResource(data);
return ResponseEntity
.ok()
.contentLength(data.length)
.header("Content-type", "application/octet-stream")
.header("Content-disposition", "attachment; filename=\"" + fileName + "\"")
.body(resource);
}
and the service method for that : -
public byte[] downloadFile(final String fileName) {
byte[] content = null;
logger.info("Downloading an object with key= " + fileName);
final S3Object s3Object = s3client.getObject(bucketName, fileName);
final S3ObjectInputStream stream = s3Object.getObjectContent();
try {
content = IOUtils.toByteArray(stream);
logger.info("File downloaded successfully.");
s3Object.close();
} catch(final IOException ex) {
logger.info("IO Error Message= " + ex.getMessage());
}
return content;
}
but I want to code for the only view not for download.
You can try this way...
AmazonS3 s3Client = new AmazonS3Client(new ProfileCredentialsProvider());
S3Object object = s3Client.getObject(new GetObjectRequest(bucketName, key));
InputStream objectData = object.getObjectContent();
BufferedImage bf = ImageIO.read(objectData);
using javax.imageio

How to change my code to Spring Boot when I have one request, and multiple returns?

If I do not use Spring Boot, my codes run well:
public void dealResult(HttpServletRequest request, HttpServletResponse response) throws Exception {
long sartTime = System.currentTimeMillis();
while (true) {
Student student = getStudentResult();
if (student != null) {
response.setContentType("text/html;charset=UTF-8");
Writer writer = response.getWriter();
String ret = student.getName() + "is ok";
writer.write(ret);
writer.close();
if (student.isAllOver) {
break;
}
if (System.currentTimeMillis() - startTime > 60000) {
response.setContentType("text/html;charset=UTF-8");
Writer writer = response.getWriter();
String ret = “Deal over time ";
writer.write(ret);
writer.close();
break;
}
}
}
}
But now I want to change my code to Spring Boot, if return one result, I can change the code like this
#PostMapping
#RequestMapping(value = {"/"}, produces = {"application/json; charset=UTF-8"})
public ResponseEntity<Object> dealResult(HttpServletRequest request) {
return new ResponseEntity<>("Deal over time", HttpStatus.OK);
}
How to change my code to have multiple returns?
Your words are multi response, mutli-response was explained at here https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages#Body_2
Seemly, what you need shouldn't called "multi-response", Do you need response in few cases differently? Separate per case in control structure, then return the corresponding HttpStatus code. You may be need something like this
#PostMapping
#RequestMapping(value = {"/"}, produces = {"application/json; charset=UTF-8"})
public ResponseEntity<Object> dealResult(HttpServletRequest request, HttpServletResponse response) throws Exception {
long sartTime = System.currentTimeMillis();
Student student = getStudentResult();
if (student != null) {
response.setContentType("text/html;charset=UTF-8");
Writer writer = response.getWriter();
String ret = student.getName() + "is ok";
writer.write(ret);
writer.close();
if (student.isAllOver) {
return new ResponseEntity<>("Deal over time", HttpStatus.OK);
} else {
if (System.currentTimeMillis() - startTime > 60000) {
response.setContentType("text/html;charset=UTF-8");
Writer writer = response.getWriter();
String ret = “Deal over time ";
writer.write(ret);
writer.close();
return new ResponseEntity<>("Deal over time", HttpStatus.REQUEST_TIMEOUT);
} else {
return new ResponseEntity<>("Error", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
}

File upload with in Spring MVC without adding any additional parameter in controller method

I am using spring boot 2. My new task is file uploading. I already did it. But I am asked to do it without adding a additional parameter to controller method like #RequestParam("files") MultipartFile files[]. I want to get this from request instead of adding this parameter.
How can I solve this?
I am adding my current code following.
#RequestMapping(value="/uploadMultipleFiles", method=RequestMethod.POST)
public #ResponseBody String handleFileUpload( #RequestParam("files") MultipartFile files[]){
try {
String filePath="c:/temp/kk/";
StringBuffer result=new StringBuffer();
byte[] bytes=null;
result.append("Uploading of File(s) ");
for (int i=0;i<files.length;i++) {
if (!files[i].isEmpty()) {
bytes = files[i].getBytes();
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(new File(filePath+files[i].getOriginalFilename())));
stream.write(bytes);
stream.close();
result.append(files[i].getOriginalFilename() + " Ok. ") ;
}
else
result.append( files[i].getOriginalFilename() + " Failed. ");
}
return result.toString();
} catch (Exception e) {
return "Error Occured while uploading files." + " => " + e.getMessage();
}
}
You can get files from HttpRequest:
#RequestMapping(value="/uploadMultipleFiles", method=RequestMethod.POST)
public String handleFileUpload(HttpRequest request){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> yourFiles = multipartRequest.getFileMap();
return "All is Ok!";
}
My sample code.
#RequestMapping(value = "/multiple/upload", method = RequestMethod.POST)
public #ResponseBody String test(#RequestParam(value = "files[]") List<MultipartFile> files,
HttpServletRequest req) {
MultipartFileWriter writer = new MultipartFileWriter();
String folderPath = "/file/";
for (MultipartFile file : files) {
writer.writeFile(file, folderPath, req);
}
return "success";
}

upload file using rest services in spring mvc

I want to upload a file( any type of file ) into a forlder using web services and spring mvc so I have a sever side and a client side.
On my client side this is the code
#RequestMapping(value = "/uploadMultipleFile", method = RequestMethod.POST , produces="application/json")
public #ResponseBody
Boolean uploadMultipleFileHandler(
#RequestParam("name") MultipartFile[] files) {
MailService ms= new MailService();
Map<String, List<ByteArrayResource>>rval = new HashMap<String, List<ByteArrayResource>>();
String message = "";
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
List<Object> files1 = new ArrayList<>();
List<Object> files2 = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
MultipartFile file = files[i];
System.out.println(file.getOriginalFilename());
try {
byte[] bytes = file.getBytes();
files1.add(new ByteArrayResource(bytes));
files2.add(file.getOriginalFilename());
//System.out.println(map.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
map.put("files", files1);
map.put("names", files2);
System.out.println(map.get("files").toString());
RestTemplate restTemplate = new RestTemplate();
String SERVER_URI="http://localhost:8080/BackEndFinalVersion";
Boolean p=restTemplate.postForObject(SERVER_URI+"/uploadMultipleFile", map, Boolean.class);
System.out.println(p.toString());
//message = message + ms.encodeFileToBase64Binary( bytes);
//rval.put("success",message);
return true;
}
and the server side code is
#RequestMapping(value = "/uploadMultipleFile", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody Boolean uploadMultipleFileHandler(#RequestParam("files") List<Object> files , #RequestParam("names") List<Object> names) {
//MailService ms= new MailService();
//Map<String, Object> rval = new HashMap<String, Object>();
String message = "";
System.out.println("looool");
System.out.println(files);
System.out.println(names);
//System.out.println(files.get(0).toString());
for (int i = 0; i < files.size(); i++) {
System.out.println(files.get(i).getClass());
String file = (String)files.get(i);
try {
byte[] bytes = file.getBytes();
//FileUtils.writeStringToFile(new File("log.txt"), file, Charset.defaultCharset());
// Creating the directory to store file
String rootPath = "C:/Users/Wassim/Desktop/uploads";
File dir = new File(rootPath);
if (!dir.exists())
dir.mkdirs();
File serverFile = new File(dir.getAbsolutePath() + File.separator + ( names.get(i)));
BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(serverFile));
stream.write(bytes);
stream.close();
//message = message + "You successfully uploaded file=" + ( (MultipartFile) files.get(i)).getOriginalFilename() + "<br />";
//FileUtils.writeByteArrayToFile(new File(dir.getAbsolutePath() + File.separator + files.get(i).getOriginalFilename()), ms.decodeFileToBase64Binary(ms.encodeFileToBase64Binary( bytes)));
//rval.put("success"+i, message);
System.out.println("noooo");
} catch (Exception e) {
message += "You failed to upload " + " => " + e.getMessage();
//rval.put("error", message);
return false;
}
}
return true;
My problem is that this code doesn't work only with .txt files
can any one support me ??

Resources