(Jhipster, front-end Angular 9 & Backend spring-boot)
My app do a xls report.
The report is done with Apache Poi and copy localy.
Now I'm trying to download the file to the client side, but I don't know how.
I want to delete the file when the download is done.
It's a post method because I send the data for the report.
Do you have any idea?
Here's my Controller:
public void createFullReport(#Valid #RequestBody ReportDTO report, HttpServletResponse response) throws IOException {
log.debug("REPORTDTO : {}", report);
File outputFile = this.reportService.makeFullReport(report);
log.debug("FILE EXIST:{}", outputFile.exists());
log.debug("IS FILE:{}", outputFile.isFile());
log.debug("FILE NAME:{}", outputFile.getName());
FileInputStream stream = new FileInputStream(outputFile);
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment; filename=" + outputFile.getName());
}
My service:
create(report: IReport): any {
console.log(report);
return this.http.post<any>(this.resourceUrl, report, { observe: 'response' });
}
My component:
this.reportService.create(this.report).subscribe((response: any) => {
console.log(response);
var blob = new Blob([response._body], { type: 'application/vnd.ms-excel' });
});
EDIT
controller:
#PostMapping("/report")
#PreAuthorize(
"hasAnyAuthority(\"" +
AuthoritiesConstants.ADMIN +
"\"+\"," +
AuthoritiesConstants.CUSTOMER_ADMIN +
"\"+\"," +
AuthoritiesConstants.INSPECTOR +
"\")"
)
public ResponseEntity createFullReport(#Valid #RequestBody ReportDTO report, HttpServletResponse response) throws IOException {
log.debug("REPORTDTO : {}", report);
XSSFWorkbook wb = (XSSFWorkbook) this.reportService.makeFullReport(report);
response.setHeader("Content-Disposition","attachment; filename=\"timesheet.xlsx\"");
writeToOutputStream(response,wb);
return ResponseEntity.ok().build();
}
private void writeToOutputStream(HttpServletResponse response,XSSFWorkbook wb){
ServletOutputStream out ;
try {
out = response.getOutputStream();
wb.write(out);
wb.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Angular service:
create(report: IReport): any {
console.log(report);
let HTTPOptions:Object = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
}),
responseType: 'blob'
}
return this.http.post<any>(this.resourceUrl, report,HTTPOptions);
}
I want to delete the file when the download is done. It's a post
method because I send the data for the report.
That would be complex and you don't need so much complexity.
Try not saving your xls file somewhere. Just create it with your apache poi as a workbook. Then write the contents of that workbook directly in your controller as a byte array.
With that you will achieve creating and delivering a xls file on the fly without the need to synchronize backend and frontend for removing it later.
#PostMapping()
public ResponseEntity createAndDeliverFile(HttpServletResponse response){
response.setHeader("Content-Disposition","attachment; filename=\"myFileName.xlsx\"");
XSSFWorkbook wb = fileWriterService.createAndReturnFile();
writeToOutputStream(response,wb);
return ResponseEntity.ok().build();
}
public void writeToOutputStream(HttpServletResponse response,XSSFWorkbook wb){
ServletOutputStream out ;
try {
out = response.getOutputStream();
wb.write(out);
wb.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
As for downloading the file your code does not seem wrong however it can be that you have slightly to adjust something if it brings any kind of error. Just add FileSaver to download the file when ready
My component:
import * as FileSaver from 'file-saver';
this.reportService.create(this.report).subscribe((response: any) => {
console.log(response);
var blob = new Blob([response._body], { type: 'application/vnd.ms-excel'});
FileSaver.saveAs(blob, 'filename' + '.xlsx;);
});
Edit after comments (It seems that conversion to Blob when data come from xlsx need extra care!)
create(report: IReport): any {
console.log(report);
let HTTPOptions:Object = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
}),
responseType: 'blob' as 'json' <----------
}
return this.http.post<any>(this.resourceUrl, report,HTTPOptions);
}
My component:
import * as FileSaver from 'file-saver';
this.reportService.create(this.report).subscribe((response: any) => {
console.log(response);
var blob = new Blob([s2ab(atob(data))], {type: ''}); <----------
FileSaver.saveAs(blob, 'filename' + '.xlsx;);
});
private s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}*
Also don't forget to send credits to this answer if it works out
blob from xlsx
Related
I am trying to use a Spring Boot RestController to download multiple pdf files.But for some reason only the first file is downloaded.The program does not throw any error.Not sure what the issue is.Is Multipart needed for this?
#RequestMapping(value = "downloadAgain", method = RequestMethod.GET)
#ResponseBody
public void newRun(HttpServletResponse response) {
String fileName1="pdf1.pdf";
String fullName1="C://Users//pdf1.pdf";
newDownloadRun(response,fileName1,fullName1);
String fileName2="pdf2.pdf";
String fullName2="C://Users//pdf2.pdf";
newDownloadRun(response,fileName2,fullName2);
}
public void newDownloadRun(HttpServletResponse response,String fileName,String fullName) {
response.setContentType("application/pdf");
response.setHeader( "Content-Disposition", "attachment;filename="+ fileName );
response.setHeader("Content-disposition", "attachment; filename=" + fileName);
try {
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
FileInputStream fis = new FileInputStream(fullName);
int len;
byte[] buf = new byte[1024];
while((len=fis.read(buf))> 0) {
bos.write(buf,0,len);
}
bos.close();
response.flushBuffer();
}catch(Exception ex) {
ex.printStackTrace();
}
}
Http protocol designed to send one file per request. if you want to send multiple files you need to prepare it as multipart/related. Look into this article https://www.motobit.com/tips/detpg_multiple-files-one-request/
I want to export Excel by browser. If I click the export button I can see information in Chrome network, but it did not download. I can download excel to my project folder, but how to export excel through the browser? Below the Ajax and controller codes.
This is my Excel util:
public class WriteExcel {
/**
* #param answerList
* #return
*/
public static void writeData(List<Answer> answerList, String paperName, HttpServletResponse response) throws IOException {
Workbook workbook = new HSSFWorkbook();
Sheet sheet = workbook.createSheet("test");
for(int i=0; i<answerList.size();i++){
Answer answer = answerList.get(i);
Row row = sheet.createRow(i);
Cell cell = row.createCell(0);
cell.setCellValue(answer.getAnswerpname());
List<AnswerReceive> answerReceives = JSON.parseArray(answer.getAnswerdata(), AnswerReceive.class);
for(int j=0; j<answerReceives.size(); j++){
AnswerReceive answerReceive = answerReceives.get(j);
Cell tmp_cell = row.createCell(j+1);
tmp_cell.setCellValue(answerReceive.getData());
}
}
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename="
.concat(String.valueOf(URLEncoder.encode(paperName, "UTF-8"))));
OutputStream out = response.getOutputStream();
workbook.write(out);
}
}
My controller:
#PostMapping("/export")
#ResponseBody
public Object exportExcel(#RequestParam("paperId") String paperId, HttpServletResponse response) throws IOException {
List<Answer> answerList = answerService.getData(paperId);
WriteExcel.writeData(answerList, "test", response);
}
My Ajax:
$("button[name='export']").click(function () {
$.ajax({
url: "/export",
type: "post",
data: {"paperId":$(this).attr("data-paper-id")},
success: function (data) {
console.log(data.flag);
console.log(data.Message);
}
})
})
Try following:
But you Apaches FileUtils for it
#PostMapping("/export", produdes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public Object exportExcel(#RequestParam("paperId") String paperId, HttpServletResponse response) throws IOException {
List<Answer> answerList = answerService.getData(paperId);
InputStream excelFile = WriteExcel.writeData(answerList, "test", response);
response.setHeader("Content-Disposition", "attachment; filename=Export" + LocalDate.now() + ".xlsx");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
FileCopyUtils.copy(excelFile, response.getOutputStream());
response.flushBuffer();
}
To create an Inputstream, attach to your writeData Funcion:
ByteArrayInputStream bais = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
workbook.write(baos);
baos.flush();
byte[] buffer = baos.toByteArray();
bais = new ByteArrayInputStream(buffer);
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
return bais;
you button should be somethin like this
<button target='_blank' href='/export'>
on server I would make this
response.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exceptions.xlsx")
response.flushBuffer();
Look at Download File Using Javascript/jQuery
Actually, if your headers are specified correctly, your file download should be started after clicking given element with corresponding href (in new tab) below code for starting download in the same tab.
I would recomend to use tools like that
http://jqueryfiledownload.apphb.com.
or through axios
axios.post("/yourUrl"
, data,
{responseType: 'blob'}
).then(function (response) {
let fileName = response.headers["content-disposition"].split("filename=")[1];
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // IE variant
window.navigator.msSaveOrOpenBlob(new Blob([response.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}),
fileName);
} else {
const url = window.URL.createObjectURL(new Blob([response.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', response.headers["content-disposition"].split("filename=")[1]); //you can set any name(without split)
document.body.appendChild(link);
link.click();
}
}
);
I am using grizzly for java rest service and consuming these web services in an android app.
Its working fine as far as "text" data is concerned.
Now I want to load the images(from server) in my android application, using this rest service and also allow the users to update image from the device.
I have tried this code
#GET
#Path("/img3")
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFile()
{
File file = new File("img/3.jpg");
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM).header("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"") // optional
.build();
}
The code above allow me to download the file, but is it possible to display result in broswer? like this
http://docs.oracle.com/javase/tutorial/images/oracle-java-logo.png
Solution of Part 1:
I have made the changes in my code as suggested by Shadow
#GET
#Path("/img3")
#Produces("image/jpg")
public Response getFile(#PathParam("id") String id) throws SQLException
{
File file = new File("img/3.jpg");
return Response.ok(file, "image/jpg").header("Inline", "filename=\"" + file.getName() + "\"")
.build();
}
Requested image will be displayed in browser
Part 2:
The code used to convert back Base64 encoded image
#POST
#Path("/upload/{primaryKey}")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces("image/jpg")
public String uploadImage(#FormParam("image") String image, #PathParam("primaryKey") String primaryKey) throws SQLException, FileNotFoundException
{
String result = "false";
FileOutputStream fos;
fos = new FileOutputStream("img/" + primaryKey + ".jpg");
// decode Base64 String to image
try
{
byte byteArray[] = Base64.getMimeDecoder().decode(image);
fos.write(byteArray);
result = "true";
fos.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return result;
}
this is my file path
public final static String BOOKINGPDFFILE= "D:/Hotels/pdf/";
This below code is what I have written to download pdf from the above resource folder
Pdf="column name in database i used for storing in database"
#RequestMapping(value = "/getpdf/{pdf}", method = RequestMethod.GET)
public void getPdf(#PathVariable("pdf") String fileName, HttpServletResponse response,HttpServletRequest request) throws IOException {
try {
File file = new File(FileConstant.BOOKINGPDFFILE + fileName+ ".pdf");
Files.copy(file.toPath(),response.getOutputStream());
} catch (IOException ex) {
System.out.println("Contract Not Found");
System.out.println(ex.getMessage());
}
}
Here is the way, hope it help.
#RequestMapping(value = "/getpdf/{pdf}", method = RequestMethod.GET)
public void getPdf(#PathVariable("pdf") String fileName, HttpServletResponse response) throws IOException {
try {
File file = new File(FileConstant.BOOKINGPDFFILE + fileName+ ".pdf");
if (file.exists()) {
// here I use Commons IO API to copy this file to the response output stream, I don't know which API you use.
FileUtils.copyFile(file, response.getOutputStream());
// here we define the content of this file to tell the browser how to handle it
response.setContentType("application/pdf");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".pdf");
response.flushBuffer();
} else {
System.out.println("Contract Not Found");
}
} catch (IOException exception) {
System.out.println("Contract Not Found");
System.out.println(exception.getMessage());
}
}
You may try something like this:
#RequestMapping(method = { RequestMethod.GET }, value = { "/downloadPdf" })
public ResponseEntity<InputStreamResource> downloadPdf()
{
try
{
File file = new File(BOOKINGPDFFILE);
HttpHeaders respHeaders = new HttpHeaders();
MediaType mediaType = MediaType.parseMediaType("application/pdf");
respHeaders.setContentType(mediaType);
respHeaders.setContentLength(file.length());
respHeaders.setContentDispositionFormData("attachment", file.getName());
InputStreamResource isr = new InputStreamResource(new FileInputStream(file));
return new ResponseEntity<InputStreamResource>(isr, respHeaders, HttpStatus.OK);
}
catch (Exception e)
{
String message = "Errore nel download del file "+idForm+".csv; "+e.getMessage();
logger.error(message, e);
return new ResponseEntity<InputStreamResource>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
And in your web page you can write the link in this way:
download PDF
You need to create an implementation of AbstractPdfView to achieve this.. You can refer this link https://www.mkyong.com/spring-mvc/spring-mvc-export-data-to-pdf-file-via-abstractpdfview/
Here is the Detailed answer for your question.
let me start with the server side code:
Below class is used to create pdf with some random content and return the equivalent byte array outputstream.
public class pdfgen extends AbstractPdfView{
private static ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
public ByteArrayOutputStream showHelp() throws Exception {
Document document = new Document();
// System.IO.MemoryStream ms = new System.IO.MemoryStream();
PdfWriter.getInstance(document,byteArrayOutputStream);
document.open();
document.add(new Paragraph("table"));
document.add(new Paragraph(new Date().toString()));
PdfPTable table=new PdfPTable(2);
PdfPCell cell = new PdfPCell (new Paragraph ("table"));
cell.setColspan (2);
cell.setHorizontalAlignment (Element.ALIGN_CENTER);
cell.setPadding (10.0f);
//cell.setBackgroundColor (new BaseColor (140, 221, 8));
table.addCell(cell);
ArrayList<String[]> row=new ArrayList<String[]>();
String[] data=new String[2];
data[0]="1";
data[1]="2";
String[] data1=new String[2];
data1[0]="3";
data1[1]="4";
row.add(data);
row.add(data1);
for(int i=0;i<row.size();i++) {
String[] cols=row.get(i);
for(int j=0;j<cols.length;j++){
table.addCell(cols[j]);
}
}
document.add(table);
document.close();
return byteArrayOutputStream;
}
}
Then comes the controller code : here the bytearrayoutputstream is converted to bytearray and sent to the client side using the response-entity with appropriate headers.
#RequestMapping(path="/home")
public ResponseEntity<byte[]> render(HttpServletRequest request , HttpServletResponse response) throws IOException
{
pdfgen pg=new pdfgen();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment:filename=report.pdf");
byte[] contents = null;
try {
contents = pg.showHelp().toByteArray();
}
catch (Exception e) {
e.printStackTrace();
}
//These 3 lines are used to write the byte array to pdf file
/*FileOutputStream fos = new FileOutputStream("/Users/naveen-pt2724/desktop/nama.pdf");
fos.write(contents);
fos.close();*/
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/pdf"));
//Here you have to set the actual filename of your pdf
String filename = "output.pdf";
headers.setContentDispositionFormData(filename, filename);
headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");
ResponseEntity<byte[]> respons = new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK);
return respons;
}
The header should be set to "application/pdf"
Then comes the client side code :
Where you can make ajax request to server to open the pdf file in new tab of the browser
$.ajax({
url:'/PDFgen/home',
method:'POST',
cache:false,
xhrFields: {
responseType: 'blob'
},
success: function(data) {
//alert(data);
let blob = new Blob([data], {type: 'application/pdf'}); //mime type is important here
let link = document.createElement('a'); //create hidden a tag element
let objectURL = window.URL.createObjectURL(blob); //obtain the url for the pdf file
link.href = objectURL; // setting the href property for a tag
link.target = '_blank'; //opens the pdf file in new tab
link.download = "fileName.pdf"; //makes the pdf file download
(document.body || document.documentElement).appendChild(link); //to work in firefox
link.click(); //imitating the click event for opening in new tab
},
error:function(xhr,stats,error){
alert(error);
}
});
Try this
#Controller
#RequestMapping("/download")
public class FileDownloadController
{
#RequestMapping("/pdf/{fileName}")
public void downloadPDFResource( HttpServletRequest request,
HttpServletResponse response,
#PathVariable("fileName") String fileName)
{
//If user is not authorized - he should be thrown out from here itself
//Authorized user will download the file
String dataDirectory = request.getServletContext().getRealPath("/WEB-INF/downloads/pdf/");
Path file = Paths.get(dataDirectory, fileName);
if (Files.exists(file))
{
response.setContentType("application/pdf");
response.addHeader("Content-Disposition", "attachment; filename="+fileName);
try
{
Files.copy(file, response.getOutputStream());
response.getOutputStream().flush();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
I am using Jasper Reports in my project for producing the reports in multiple formats. Although all the code examples run fine, I have ran into a conceptual issue.
I wrote this trivial code which produces a PDF at the browser as a download option:
#GET
#Path("/basicdbreport")
#Produces("application/pdf")
public Response basicDbReport(#Context ServletContext context,
#Context HttpServletResponse response) throws JRException,
IOException {
Connection connection = null;
byte[] reportBytes = null;
String reportName = "firstsqlexample";
File file = new File(path + reportName + JASPER_EXTENSION);
// check if compiled report exists
if (!file.exists()) {
compileReport(context.getRealPath(path + reportName + JRXML_EXTENSTION));
}
// input stream for filling the compiled report
InputStream compiledReportStream = context.getResourceAsStream(path
+ reportName + JASPER_EXTENSION);
try {
connection = dataSource.getConnection();
reportBytes = JasperRunManager.runReportToPdf(compiledReportStream,
new HashMap<String, Object>(), connection);
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (reportBytes != null) {
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(reportBytes);
}
ResponseBuilder restResponse = Response.ok();
restResponse.header("Content-Disposition",
"attachment; filename=firstSQLReport.pdf");
return restResponse.build();
}
The code runs fine and I get a download prompt at the browser. However, when I dug deeper into the Jasper API I found a method runReportToPdfStream() method which would handle the output stream for me.
The new code looks something like this:
#GET
#Path("/basicdbreport")
#Produces("application/pdf")
public Response basicDbReport(#Context ServletContext context,
#Context HttpServletResponse response) throws JRException,
IOException {
ServletOutputStream outputStream = response.getOutputStream();
Connection connection = null;
String reportName = "firstsqlexample";
File file = new File(path + reportName + JASPER_EXTENSION);
// check if compiled report exists
if (!file.exists()) {
compileReport(context.getRealPath(path + reportName + JRXML_EXTENSTION));
}
// input steam to fill complied report
InputStream compiledReportStream = context.getResourceAsStream(path
+ reportName + JASPER_EXTENSION);
try {
connection = dataSource.getConnection();
JasperRunManager.runReportToPdfStream(compiledReportStream, outputStream, new HashMap<String, Object>(), connection);
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
ResponseBuilder restResponse = Response.ok();
restResponse.header("Content-Disposition",
"attachment; filename=firstSQLReport.pdf");
return restResponse.build();
}
The code runs fine but I do not get any download prompt, the pdf gets rendered on the browser instead. The response headers are as follows (on the browser):
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
Date: Wed, 21 Aug 2013 05:51:42 GMT
What's the reason that the code now fails to provide a download prompt? I am not an ace with HTTP but I guess this:
restResponse.header("Content-Disposition",
"attachment; filename=firstSQLReport.pdf");
is responsible for the download option. Its nowhere in the response although I did include it in the code. Please advice.
yes, you are right, Content-Disposition is the response header that you need to set to trigger the download action on the client browser.
i think you need to set the response headers first before writing to the response output stream.