Returning excel file using spring boot controller - excel

I was trying to make a rest endpoint in Spring Boot which reads from DB, generates an excel file(Using Apache POI) which is returned to the user using HttpServletResponse but when I invoke this, the excel is getting created but it's not downloading. I had some other code earlier in place which was working fine but I accidentally removed that and now I'm stuck. Any help/leads are appreciated.
#RequestMapping(path = "/save", method = RequestMethod.GET)
public ResponseEntity<String> saveToXls(#RequestParam String id, #RequestParam String appName, HttpServletResponse response) {
AppInstance appInstance = appInstanceRepo.get(id);
List<DownloadDetail> downloadDetailList = downloadDAO.searchByInstanceId(id);
//List<DownloadDetail> downloadDetailList = appInstance.getDownloads();
System.out.print("LIST SIZE:" + downloadDetailList.size());
String fileName = appName + " report";
File myFile = new File(fileName + ".xls");
FileOutputStream fileOut;
downloadDetailList.forEach(downloadDetail -> System.out.print(downloadDetail.getSid()));
try {
try (HSSFWorkbook workbook = new HSSFWorkbook()) {
HSSFSheet sheet = workbook.createSheet("lawix10");
HSSFRow rowhead = sheet.createRow((short) 0);
rowhead.createCell((short) 0).setCellValue("SID");
rowhead.createCell((short) 1).setCellValue("Download Time");
rowhead.createCell((short) 2).setCellValue("OS Version");
int i = 0;
for (DownloadDetail downloadDetail : downloadDetailList) {
System.out.print("In loop -2");
HSSFRow row = sheet.createRow((short) i);
row.createCell((short) 0).setCellValue(downloadDetail.getSid());
row.createCell((short) 1).setCellValue(downloadDetail.getDownloadTime());
row.createCell((short) 2).setCellValue(downloadDetail.getOsVersion());
i++;
}
fileOut = new FileOutputStream(myFile);
workbook.write(fileOut);
}
fileOut.close();
byte[] buffer = new byte[10240];
response.addHeader("Content-disposition", "attachment; filename=test.xls");
response.setContentType("application/vnd.ms-excel");
try (
InputStream input = new FileInputStream(myFile);
OutputStream output = response.getOutputStream();
) {
for (int length = 0; (length = input.read(buffer)) > 0;) {
output.write(buffer, 0, length);
}
}
response.flushBuffer();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
EDIT:
I tried to do it another way as shown below:
try (InputStream is = new FileInputStream(myFile)) {
response.addHeader("Content-disposition", "attachment; filename=test.xls");
response.setContentType("application/vnd.ms-excel");
IOUtils.copy(is, response.getOutputStream());
}
response.flushBuffer();
This also doesn't seem to cut it.

This is a my example. Probably the issue is how you manage the OutputStream:
ServletOutputStream os = response.getOutputStream();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment; filename=\""+fileName+".xls\"");
workbook = excelStrategyMap.get(strategy).export(idList, status, params);
workbook.write(os);
workbook.close();
os.flush();
response.flushBuffer();

Once you get the workbook file, set the file name and file type. and add the response header and content type as mentioned below.
Then write the file to the response and flush it's buffer.
XSSFWorkbook file = excelUploadService.downloadDocument();
String filename = "Export.xlsx";
String filetype = "xlsx";
response.addHeader("Content-disposition", "attachment;filename=" + filename);
response.setContentType(filetype);
// Copy the stream to the response's output stream.
file.write(response.getOutputStream());
response.flushBuffer();
In the client side, get the response from the REST API and set the content type received by the response object. Using FileSaver library save the file into your local file system.
Here is the documentation for FileSaver js -- File saver JS Library
var type = response.headers("Content-Type");
var blob = new Blob([response.data], {type: type});
saveAs(blob, 'Export Data'+ '.xlsx');

#GetMapping(value = "/", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
#ResponseBody
public byte[] generateExcel() {
byte[] res = statisticsService.generateExcel();
return res;

Related

Timeout when generate excel for download

I am trying to export Excel using poi and servlet. code like this
SXSSFWorkbook workbook = genrateExcel(id)
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
byte[] outArray = outputStream.toByteArray();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setContentLength(outArray.length);
response.setHeader("Expires:", "0");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(excelFileName, "UTF-8"));
OutputStream outStream = response.getOutputStream();
outStream.write(outArray);
outStream.flush();
workbook.dispose();
workbook.close();
But sometimes generating costs too much time,so i get a 504 timeout error.
I wonder how can I hold the connection until file is generated ?
UPDATE:
thanks Gagravarr. I update the code to this
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<SXSSFWorkbook> future = threadPool.submit(new Callable<SXSSFWorkbook>() {
#Override
public SXSSFWorkbook call() {
return genrateExcel(id);
}
});
SXSSFWorkbook workbook = null;
try {
response.setHeader("Expires:", "0");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(excelFileName, "UTF-8"));
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
OutputStream outStream = response.getOutputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
int times = 0;
while (times < 50) {
if (future.isDone()) {
workbook = future.get();
break;
}
outStream.flush();
Thread.sleep(5000);
times++;
}
workbook = workbook == null ? new SXSSFWorkbook() : workbook;
workbook.write(outputStream);
byte[] outArray = outputStream.toByteArray();
response.setContentLength(outArray.length);
outStream.write(outArray);
outStream.flush();
workbook.dispose();
workbook.close();
} catch (Exception e) {
e.printStackTrace();
}
But got a ClientAbortException: java.io.IOException meanwhile Chrome report the page isnot working ERR_INCOMPLETE_CHUNKED_ENCODING is sth wrong?

spring mvc export excel failed sometimes and data displays garblend on the page

There is a data report as the following in my project, I want to export it as excel:
But the wierd thing is that sometimes it exports successfully and sometimes failed. I have tried many times and it turns out that when the excel sheet is over 17 lines, it will lead to this:
the page redirects to a new page with garblend data.
the post request has been changed to get request.
It's sure that the excel is successfully created even if it exports failed, because I have written it to disk and checked the file. The following is the controller, what goes wrong with me?
#RequestMapping("/download")
public void download(HttpServletRequest request, HttpServletResponse response, ReportCondition condition){
try {
List<HashMap<String, String>> mapList = reportFormService.find(condition);
if(mapList == null || mapList.size() == 0){
logger.info("No reports...");
}
Map<String, Date> dateMap = DateConditionUtil.getStartEndDate1(condition.getStartDate(), condition.getEndDate());
String startDate = DateFormatUtils.format(dateMap.get("startDate"), "yyyyMMdd");
String endDate = DateFormatUtils.format(dateMap.get("endDate"), "yyyyMMdd");
String[] titleArr = new String[]{"序号","日期","应用系统","短信服务商","请求发送总数","请求成功数量","实际短信条数","实际成功条数","费用","失败数量","成功率"};
String[] fieldArr = new String[]{"ID","RECORDDATE","APPNAME","PROVIDERNAME","SENDCOUNT","SUCCESSCOUNT","SENDSUM","SUCCESSSUM","TOTALFEE","FAILURECOUNT","SUCCESSRATE"};
ByteArrayOutputStream os = new ByteArrayOutputStream();
WorkBookUtil.createExcel(titleArr, fieldArr, mapList, os);
try(BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(os.toByteArray()));
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())){
byte[] buff = new byte[1024];
int bytes;
while (-1 != (bytes = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytes);
}
bos.flush();
}catch (Exception e){
logger.error(e);
}
response.setCharacterEncoding("utf-8");
response.setHeader("content-disposition", "attachment;filename=" + String.format("%s-%s.xls", startDate, endDate));
response.setContentType("application/vnd.ms-excel;charset=utf-8");
} catch (IOException e){
logger.error("Export failed", e);
}
}
You should set the response headers first before you create the .xls file. So try this:
response.setCharacterEncoding("utf-8");
response.setHeader("content-disposition", "attachment;filename=" + String.format("%s-%s.xls", startDate, endDate));
response.setContentType("application/vnd.ms-excel;charset=utf-8");
ByteArrayOutputStream os = new ByteArrayOutputStream();
WorkBookUtil.createExcel(titleArr, fieldArr, mapList, os);
// ...
Try adding your response headers before writing the bytes out to the user. It would help to add a content-length header as well. You can get that after your call to createExcel with os.toByteArray().length. In addition close() your bos after you flush() it.

why downloading to file is not working in jsf? [duplicate]

This question already has answers here:
How to provide a file download from a JSF backing bean?
(5 answers)
Closed 5 years ago.
i made a call to download() method to save json into xml with extension ".svg". The jsondata is global variable store json.
public void download(){
File file = exportFile(jsondata);
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
writeOutContent(response, file, file.getName());
FacesContext.getCurrentInstance().responseComplete();
FacesContext.getCurrentInstance().renderResponse();
}
and the exportFile(jsondata) is
public File exportFile(String jsonData){
File xmlFile = null;
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
JSONObject jsonObject = new JSONObject(jsonData);
Element root = doc.createElement("web");
doc.appendChild(root);
Element rootElement1 = doc.createElement("class");
rootElement1.appendChild(doc.createTextNode(jsonObject.getString("class")));
root.appendChild(rootElement1);
JSONArray jsonArray1 = (JSONArray) jsonObject.get("nodes");
Element rootElement2 = doc.createElement("nodes");
root.appendChild(rootElement2);
for (int i = 0; i < jsonArray1.length(); i++) {
Element staff = doc.createElement("node");
rootElement2.appendChild(staff);
JSONObject childObject = (JSONObject) jsonArray1.get(i);
Iterator<String> keyItr = childObject.keys();
while (keyItr.hasNext()) {
String key = keyItr.next();
Element property = doc.createElement(key);
property.appendChild(doc.createTextNode(childObject.getString(key)));
staff.appendChild(property);
}
}
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
//for pretty print
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(doc);
xmlFile = new File("file.svg");
//write to console or file
// StreamResult console = new StreamResult(System.out);
StreamResult file = new StreamResult(xmlFile);
//write data
// transformer.transform(source, console);
transformer.transform(source, file);
} catch (Exception pce) {
pce.printStackTrace();
}
return xmlFile;
}
finally to write this one file writeOutContent()
public void writeOutContent(final HttpServletResponse res, final File content, final String theFilename) {
if (content == null) {
System.out.println("content is null");
return;
}
try {
res.setHeader("Content-Disposition", "attachment; filename=\"" + theFilename + "\"");
System.out.println("res " + res.getHeader("attachment; filename=\"" + theFilename + "\""));
res.setContentType("application/octet-stream");
FileInputStream fis = new FileInputStream(content);
OutputStream os = res.getOutputStream();
int bt = fis.read();
while (bt != -1) {
os.write(bt);
bt = fis.read();
}
os.flush();
fis.close();
os.close();
} catch (Exception ex) {
Logger.getLogger(DownloadFile.class.getName()).log(Level.SEVERE, null, ex);
}
}
i can see the xml in console but what am doing wrong that its not downloading?? please help me.
thanks in advance.
i got the mistake. it was not in above code. if we make through commandLink then it won't work but if make call through commandButton then it worked. if you want know know more read difference between commandButton vs commandLink

Writing a binary response (stream) directly to MIME in a Notes document

As I'm playing around with the Watson API I am using the Text2Speech service to get an audio stream (file) from the service. I already get the file with the code but my MIME doesn't contain anything later. I save the document after I call this method below. Any best pratices for streaming a byte content directly to a MIME would be appreciated.
public void getSpeechFromText(AveedoContext aveedoContext, Document doc, String fieldName, String text, String voiceName) {
try {
Session session = aveedoContext.getXspHelper().getSession();
session.setConvertMIME(false);
TextToSpeech service = new TextToSpeech();
String username = watsonSettings.getTextToSpeechUsername();
String password = watsonSettings.getTextToSpeechPassword();
if (username.isEmpty() || password.isEmpty()) {
throw new AveedoException("No credentials provided for service");
}
service.setUsernameAndPassword(username, password);
MIMEEntity mime = doc.getMIMEEntity(fieldName);
if (mime == null) {
mime = doc.createMIMEEntity(fieldName);
}
// local proxy?
if (!Util.isEmpty(customEndpoint)) {
// service.setEndPoint(customEndpoint + "/speech/");
}
Voice voice = Voice.getByName(voiceName);
AudioFormat audio = AudioFormat.WAV;
System.out.println("Fieldname: " + fieldName + "SPEECH: " + text + ", Voice: " + voice.getName() + ", Format: "
+ audio.toString());
InputStream stream = service.synthesize(text, voice, audio).execute();
InputStream in = WaveUtils.reWriteWaveHeader(stream);
Stream out = session.createStream();
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer);
}
mime.setContentFromBytes(out, "audio/wav", MIMEEntity.ENC_IDENTITY_BINARY);
out.close();
session.setConvertMIME(true);
in.close();
stream.close();
} catch (Throwable e) {
aveedoLogger.error("Error calling Watson service (text to speech)", e);
e.printStackTrace();
}
}
I believe that you need to create a child MIME entity. I use the following code in one of my apps to attach an image:
boolean convertMime = JSFUtil.getSessionAsSigner().isConvertMime();
if (convertMime) {
JSFUtil.getSessionAsSigner().setConvertMime(false);
}
final MIMEEntity body = doc.createMIMEEntity(getAttachmentFieldName());
// Add binary attachment
final MIMEEntity attachmentChild = body.createChildEntity();
final MIMEHeader bodyHeader = attachmentChild.createHeader("Content-Disposition");
bodyHeader.setHeaderVal("attachment; filename=" + incident.getPhoto().getName());
Stream imgStream = getPhoto(doc, incident.getPhoto());
attachmentChild.setContentFromBytes(imgStream, incident.getPhoto().getType(), MIMEEntity.ENC_IDENTITY_BINARY);
imgStream.close();
imgStream.recycle();
imgStream = null;
if (convertMime) {
JSFUtil.getSessionAsSigner().setConvertMime(true);
}

POI not sending xls file as attachment , sending as string

one of my junior code is not sending excel file as attachment . It is sending file like
------=_Part_0_2066339629.1374147892060
Content-Type: text/plain; name="Service_Change_Alert_Thu Jul 18 17:14:50 IST 2013.xlsx"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="Service_Change_Alert_Thu Jul 18 17:14:50 IST 2013.xlsx"
UEsDBBQACAAIANqJ8kIAAAAAAAAAAAAAAAARAAAAZG9jUHJvcHMvY29yZS54bWytkV1LwzAUhu/7
K0Lu2yTr1BHaDlEGguLADsW7kB7bYvNBEu3892bdrCheennyPu/D4aRY79WA3sH53ugSs4xiBFqa
ptdtiXf1Jl3hdZUkhTQOts5YcKEHj2JL+xJ3IVhOiJcdKOGzGOuYvBinRIija4kV8lW0QBaUnhMF
QTQiCHKwpXbW4aOPS/vvykbOSvvmhknQSAIDKNDBE5Yx8s0GcMr/WZiSmdz7fqbGcczGfOLiRow8
3d0+TMunvfZBaAm4ShAqTnYuHYgADYoOHj4slPgrecyvrusNrhaU5Sm9SNmqZowvl/yMPhfkV//k
function is following
public void sendSeviceabilityMail(List<OctpinSaveBean> datalist)
throws MessagingException {
Session session = null;
Map<String, String> utilsMap = ApplicationBean.utilsProperties;
if (utilsMap == null || utilsMap.size() == 0)
utilsMap = ApplicationBean.getUtilsPropertyFileValues();
smtpHost = utilsMap.get("SMTPHost");
to = utilsMap.get("To");
try {
if (smtpHost != null && to != null) {
Date date = new Date();
XSSFWorkbook updateDataBook =
updatedServiceabilityExcel(datalist);
Properties props = System.getProperties();
props.put("mail.smtp.host", smtpHost);
props.put("To", to);
session = Session.getInstance(props, null);
String str = "strstr";
Multipart multipart = new MimeMultipart();
BodyPart messageBodyPart1 = new MimeBodyPart();
messageBodyPart1.setContent(str, "text/html");
BodyPart messageBodyPart = new MimeBodyPart();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
updateDataBook.write(baos);
byte[] bytes = baos.toByteArray();
DataSource ds = new ByteArrayDataSource(bytes,
"application/vnd.ms-excel");
DataHandler dh = new DataHandler(ds);
messageBodyPart.setDataHandler(dh);
String fileName = "Service_Change_Alert_" + date+".xlsx";
messageBodyPart.setFileName(fileName);
messageBodyPart.setHeader("Content-disposition", "attachment;
filename=\"" + fileName + "\"");
multipart.addBodyPart(messageBodyPart1);
multipart.addBodyPart(messageBodyPart);
if (to != null && to.length() > 0) {
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("tech_noida
<tech_noida#xyz.com>"));
String[] toArray = to.split(",");
InternetAddress[] address = new
InternetAddress[toArray.length];
for (int i = 0; i < toArray.length; i++) {
address[i] = new InternetAddress(toArray[i]);
}
msg.setRecipients(Message.RecipientType.TO, address);
msg.setSubject("Updated Serviceability Alert!!!");
msg.setContent(multipart);
msg.setSentDate(date);
try {
Transport.send(msg);
logger.info("Mail has been sent about the updated serviceability alert to :" + to);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
The problem is not related to POI, but with JavaMail (plus the way different email clients handles the specs and malformed emails). Try this:
Multipart multipart = new MimeMultipart();
MimeBodyPart html = new MimeBodyPart();
// Use actual html not "strstr"
html.setContent("<html><body><h1>Hi</h1></body></html>", "text/html");
multipart.addBodyPart(html);
// ...
// Joop Eggen suggestion to avoid spaces in the file name
String fileName = "Service_Change_Alert_"
+ new SimpleDateFormat("yyyy-MM-dd_HH:mm").format(date) + ".xlsx";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
updateDataBook.write(baos);
byte[] poiBytes = baos.toByteArray();
// Can be followed by the DataSource / DataHandler stuff if you really need it
MimeBodyPart attachment = new MimeBodyPart();
attachment.setFileName(filename);
attachment.setContent(poiBytes, "application/vnd.ms-excel");
//attachment.setDataHandler(dh);
attachment.setDisposition(MimeBodyPart.ATTACHMENT);
multipart.addBodyPart(attachment);
Update:
Also don't forget to call saveChanges to update the headers before sending the message.
msg.saveChanges();
See this answer for further details.

Resources