I have some data in the DB and would like to be able to export it to a CSV file and provide a link so the user can download it.
Is there any mechanism provided by Spring 3 for this?
Do you know how could I do that?
I guess it should be easy to build a CSV or any text file view. Here are suggested steps:
Create a View class (you can name it as CSVView) extending org.springframework.web.servlet.view.AbstractView
Override renderMergedOutputModel as follows (pseudo code):
BufferedWriter writer = new BufferedWriter(response.getWriter())
response.setHeader("Content-Disposition","attachment; filename=\"file.csv\"");
myDbData = (Whatever) modelMap.get("modelKey");
some kind of loop {writer.write(myDbData csv row); writer.newLine(); }
finally writer.flush(); writer.close();
After that just return ModelAndView with model (object Whatever with modelKey) and view as CSVView in the controller.
Related
Hi I have created a spring boot application to generate csv files that fetch data from database and write them into a csv file. Also I have added the functionality to select the columns that we need from the database to be included in the csv. Now I need to download multiple files with different columns from the database. I tried to repeat the code to generate csv in the same class but it simply adds the content required in the second file in the first file.Please let me know what can I do. Example if there are 4 columns:-id, amount, currency,name then id amount in 1 file name and currency in another.following is my code
Controller:
public void exportCSV(#RequestParam(name="cohort") String cohort ,HttpServletResponse response) throws Exception {
// set file name and content type
String filename = "liabilities.csv";
response.setContentType("text/csv");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"");
// Configure the CSV writer builder
StatefulBeanToCsvBuilder<Report> builder = new StatefulBeanToCsvBuilder<Report>(response.getWriter()).withQuotechar(CSVWriter.NO_QUOTE_CHARACTER).withSeparator(CSVWriter.DEFAULT_SEPARATOR).withOrderedResults(true);
// Ignore any field except the `id` and `amount` ones
Arrays.stream(Report.class.getDeclaredFields())
.filter(field -> !("id".equals(field.getName()) || "amount".equals(field.getName())))
.forEach(field -> builder.withIgnoreField(Report.class, field));
// create a csv writer
StatefulBeanToCsv<Report> writer = builder.build();
// write all employees to csv file
writer.write(reportsService.findByCohort(cohort));
}
}
The above code generates csv file with Id and amount entries from the database now what should i do to get currency and name in one csv and download it simultaneously.
i'm doing a simple batch job with Spring Batch and Spring Boot.
I need to read a flat file, separate the header data (first line) from the body data (rest of lines) for individual business logic processing and then write everything into a single file.
As you can see, the header has 5 params that have to be mapped to one class, and the body has 12 which have to be mapped to a different one.
I first thought of using FlatFileItemReader and skip the header. Then use the skippedLinesCallback to handle that line, but i couldn't figure out how to do it.
I'm new to Spring Batch and Java Config. If someone can help me writing a solution for my problem i would really aprecciate it!
I leave here the input file:
01.01.2017|SUBDCOBR|12:21:23|01/12/2016|31/12/2016
01.01.2017|12345678231234|0002342434|BORGIA RUBEN|27-32548987-9|FA|A|2062-
00010443/444/445|142,12|30/08/2017|142,01
01.01.2017|12345673201234|2342434|ALVAREZ ESTHER|27-32533987-9|FA|A|2062-
00010443/444/445|142,12|30/08/2017|142,02
01.01.2017|12345673201234|0002342434|LOPEZ LUCRECIA|27-32553387-9|FA|A|2062-
00010443/444/445|142,12|30/08/2017|142,12
01.01.2017|12345672301234|0002342434|SILVA JESUS|27-32558657-9|NC|A|2062-
00010443|142,12|30/08/2017|142,12
Cheers!
EDIT 1:
This would be my first attepmt . My "body" POJO is called DetalleFacturacion and my "header" POJO is CabeceraFacturacion. The reader I thought to do it with DetalleFacturacion pojo, so i can skip the header and treat it later... however i'm not sure how to assign header's data into CabeceraFacturacion.
public FlatFileItemReader<DetalleFacturacion> readerDetalleFacturacion(){
FlatFileItemReader<DetalleFacturacion> reader = new FlatFileItemReader<>();
reader.setLinesToSkip(1);
reader.setResource(new ClassPathResource("/inputFiles/GLEO-MN170100-PROCESO01-SUBDFACT-000001.txt"));
DefaultLineMapper<DetalleFacturacion> detalleLineMapper = new DefaultLineMapper<>();
DelimitedLineTokenizer tokenizerDet = new DelimitedLineTokenizer("|");
tokenizerDet.setNames(new String[] {"fechaEmision", "tipoDocumento", "letra", "nroComprobante",
"nroCliente", "razonSocial", "cuit", "montoNetoGP", "montoNetoG3",
"montoExento", "impuestos", "montoTotal"});
LineCallbackHandler skippedLineCallback = new LineCallbackHandler() {
#Override
public void handleLine(String line) {
String[] headerSeparado = line.split("|");
String printDate = headerSeparado[0];
String reportIdentifier = headerSeparado[1];
String tituloReporte = headerSeparado[2];
String fechaDesde = headerSeparado[3];
String fechaHasta = headerSeparado[4];
CabeceraFacturacion cabeceraFacturacion = new CabeceraFacturacion();
cabeceraFacturacion.setPrintDate(printDate);
cabeceraFacturacion.setReportIdentifier(reportIdentifier);
cabeceraFacturacion.setTituloReporte(tituloReporte);
cabeceraFacturacion.setFechaDesde(fechaDesde);
cabeceraFacturacion.setFechaHasta(fechaHasta);
}
};
reader.setSkippedLinesCallback(skippedLineCallback);
detalleLineMapper.setLineTokenizer(tokenizerDet);
detalleLineMapper.setFieldSetMapper(new DetalleFieldSetMapper());
detalleLineMapper.afterPropertiesSet();
reader.setLineMapper(detalleLineMapper);
// Test to check if it is saving correctly data in CabeceraFacturacion
CabeceraFacturacion cabeceraFacturacion = new CabeceraFacturacion();
System.out.println("Print Date:"+cabeceraFacturacion.getPrintDate());
System.out.println("Report Identif:
"+cabeceraFacturacion.getReportIdentifier());
return reader;
}
You are correct . You need to use skippedLinesCallback to handle skip lines.
You need to implement LineCallbackHandler interface and add you processing in handleLine method.
LineCallbackHandler Interface passes the raw line content of the lines in the file to be skipped. If linesToSkip is set to 2, then this interface is called twice.
This is how you can define Reader for the same.
Java Config - Spring Batch 4
#Bean
public FlatFileItemReader<POJO> myReader() {
return FlatFileItemReader<pojo>().
.setResource(new FileSystemResource("resources/players.csv"));
.name("myReader")
.delimited()
.delimiter(",")
.names("pro1,pro2,pro3")
.targetType(POJO.class)
.skippedLinesCallback(skippedLinesCallback)
.build();
}
I have used Export to Excel Functionality as below:
var table = getReport(param1, param2, param3);
Response.ClearContent();
Response.AddHeader("content-disposition", "attachment;filename=Report.xls");
Response.ContentType = "application/vnd.ms-excel";
System.Web.UI.WebControls.GridView grd = new System.Web.UI.WebControls.GridView();
grd.DataSource = table; // give datasource here
grd.DataBind();
StringWriter swr = new StringWriter();
HtmlTextWriter tw = new HtmlTextWriter(swr);
grd.RenderControl(tw);
Response.Write(swr.ToString());
Response.End();
return View();
where getReport returns data in table format.
Now this creates a new excel file but i want to use existing excel template to create excel output.
How to to add such template to project and how to bind data with it?
The approach you are using now is writing an HTML string to the Response and setting the ContentType to Excel in the Response headers. This does not create a true Excel file, it just forces the browser to launch the file in Excel, and Excel is able to handle HTML files. There is no way to add a template with this approach.
If you want to use an existing Excel file as a template, you will need to use an API that can create and modify real Excel files, such as OfficeWriter. Here is an example of how to bind your DataTable directly to a template file using OfficeWriter.
using SoftArtisans.OfficeWriter.ExcelWriter;
ExcelTemplate xlt = new ExcelTemplate();
//Open the template file
xlt.Open(pathToTemplateFile);
//Create a DataBindingProperties object
DataBindingProperties props = xlt.CreateDataBindingProperties();
// Call the BindData method, passing the DataTable and a name for the datasource
// which will be referenced in the "data markers" in the template
xlt.BindData(table, "DataSource1", props);
//Call the Process method, which binds the data and generates the output file
xlt.Process();
//Stream the file to the Response
xlt.Save(Response, "Report.xls", false);
Disclaimer: I work for SoftArtisans, the makers of OfficeWriter
I have this controller methods that depending on the parameters introduced by the user downloads a certain PDF file and shows a view with its different pages converted to PNG.
So the way I approached it works like this:
First I map a method to receive the post data sent by the user, then generate the URL of the actual PDF converter and pass it to the model:
#RequestMapping(method = RequestMethod.POST)
public String formPost(Model model, HttpServletRequest request) {
//Gather parameters and generate PDF url
Long idPdf = Long.parseLong(request.getParam("idPdf"));
//feed the jsp the url of the to-be-generated image
model.addAttribute("image", "getImage?idPdf=" + idPdf);
}
Then in getImageMethod I download the PDF and then generate a PNG out of it:
#RequestMapping("/getImage")
public HttpEntity<byte[]> getPdfToImage(#RequestParam Long idPdf) {
String url = "myPDFrepository?idPDF=" + idPdf;
URL urlUrl = new URL(url);
URLConnection urlConnection;
urlConnection = urlUrl.openConnection();
InputStream is = urlConnection.getInputStream();
return PDFtoPNGConverter.convert(is);
}
My JSP just has an img tag that refers to this url:
<img src="${image}" />
So far this work perfectly. But now I need to allow the possibility of viewing multi page PDFs, converted as PNGS, each of them in a different page. So I would add a page parameter, then feed my model with the image url including that page parameter, and in my getImage method I would convert only that page.
But the way it is implemented, I would be downloading the PDF again for each page, plus an additional time for the view, so it can find out whether this specific PDF has more pages and then show the "prev" and "next" buttons.
What would be a good way to preserve the same file during these requests, so I download it just once? I thought about using temp files but then managing its deletion might be a problem. So maybe storing the PDF in the session would be a good solution? I don't even know if this is good practice or not.
I am using Spring MVC by the way.
I think the simplest way would be using spring cache abstraction. Look at tutorial and will need to change your code a little: move logic that load pdf to separate class.
it will looks like:
interface PDFRepository {
byte[] getImage(long id);
}
#Repository
public class PDFRepositoryImpl implements PDFRepository {
#Cacheable
public byte[] getImage(long id) {
String url = "myPDFrepository?idPDF=" + idPdf;
URL urlUrl = new URL(url);
URLConnection urlConnection;
urlConnection = urlUrl.openConnection();
InputStream is = urlConnection.getInputStream();
return PDFtoPNGConverter.convert(is);
}
}
You will get pluggable cache implementation support and good cache expiration management.
I am a newbie to grails currently working on a web-app project that works with users uploading pictures. Users can create a "hunt" with a list of prompts. Each "prompt" is the objective for participants (eg: upload pictures of your favorite candy.) Basically the web-app is a "scavenger hunt" tool for Photographers to socially share their work.
Right now, I am having trouble trying write a function in my user controller to generate a zip file with all of the pictures that the user has uploaded. This is what my controller function looks like at the moment.
I used this example I found to start.
Generating a zip file.
def downloadAlbum(){
ByteArrayOutputStream baos = new ByteArrayOutputStream()
ZipOutputStream zipFile = new ZipOutputStream(baos)
//Instance of a user domain class
def userInstance = User.findByLogin(auth.user())
//pictures are uploaded to a prompt and stored as a photoInstance
//this line of code gets the actual file stored as byte[]
photoInstance.myFile = image.getBytes()
//select all photos that belong to the user and store them into a list
def photoInstanceList = userInstance ? Photo.findAllByMyUser(userInstance) : []
//Mapping
[userInstance: userInstance, photoInstanceList: photoInstanceList]
photoInstanceList.each {photo ->
if (photoInstance.myFile != "") {
File file = new File(photoInstance.myFile)
zipFile.putNextEntry(new ZipEntry(Photo.title+".jpeg"))
file.withInputStream { i ->
zipFile << i
}
zipFile.closeEntry()
}
}
zipFile.finish()
response.setHeader("Content-disposition", "filename=\"${login}.zip\"")
response.contentType = "application/zip"
response.outputStream << baos.toByteArray()
response.outputStream.flush()
}
I then use this code in the User view to generate a link that calls the function in the user controller. Am I close? Is there some missing piece in my code?
By the way, This is the first question I have ever written on Stack Overflow. I appreciate the time that you have taken to read this entry. If anything is not clear enough, please where I can improve this question. I am using grails 2.1.1
Thank you for your time!!
If the above code has been copied as is here, shouldn't we be using photo.title instead of Photo.title in the below line
zipFile.putNextEntry(new ZipEntry(Photo.title+".jpeg"))