Spring Boot: Excel, Csv and Pdf view example
Nowadays, exporting data into different format (Csv, Excel, Pdf ...) is a very general requirement in the most of any project. In this article, we demonstrate how to create an Excel, PDF and CSV views using Spring Boot. When configured properly, a Spring’s view resolver can generate the requested document from model data and send it to the client for downloading. the complete code can be found here.
Spring MVC View Configuration
First thing, We create a WebConfig
class, Annotated with @Configuration
to mark this class as a configuration file. I'm using contentNegotiatingViewResolver
, which tells web controllers to return ModelAndViews or view names and based on various criteria, choose the right data representation strategy.
The highest priority hereby has the file extension which is used if available in the request. Next, the ViewResolver will look for a (definable) request parameter that identifies the view. If that does not help, the ViewResolver uses the Java Activation Framework to determine the Content-Type. If all fails, use the the HTTP Accept header. Of course the steps can be individually disabled. Check out this great article for more details.
In our example, we will be using the URL extension to help determine the media types. Also, we have set the default media type to TEXT_JSON in absence of file extension or when the filetype is unknown.
Also, We need to set the ContentNegotiationManager
which will be injected by Spring, and different resolvers for each possible output format our application might produce.
Finally, we have created different view resolvers for PDF, XLS and CSV output which we will discuss next.
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.defaultContentType(MediaType.APPLICATION_JSON)
.favorPathExtension(true);
}
/*
* Configure ContentNegotiatingViewResolver
*/
@Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
// Define all possible view resolvers
List<ViewResolver> resolvers = new ArrayList<>();
resolvers.add(csvViewResolver());
resolvers.add(excelViewResolver());
resolvers.add(pdfViewResolver());
resolver.setViewResolvers(resolvers);
return resolver;
}
/*
* Configure View resolver to provide XLS output using Apache POI library to
* generate XLS output for an object content
*/
@Bean
public ViewResolver excelViewResolver() {
return new ExcelViewResolver();
}
/*
* Configure View resolver to provide Csv output using Super Csv library to
* generate Csv output for an object content
*/
@Bean
public ViewResolver csvViewResolver() {
return new CsvViewResolver();
}
/*
* Configure View resolver to provide Pdf output using iText library to
* generate pdf output for an object content
*/
@Bean
public ViewResolver pdfViewResolver() {
return new PdfViewResolver();
}
}
Creating Controller
Nothing much to say here, The Export
controller adds some data to the Model which we’ll display on the views.
@Controller
public class Export {
@Autowired
UserService userService;
/**
* Handle request to download an Excel document
*/
@RequestMapping(value = "/download", method = RequestMethod.GET)
public String download(Model model) {
model.addAttribute("users", userService.findAllUsers());
return "";
}
}
Excel View
There are 2 file formats in which we can create an Excel document. The .xls
is the old format, the .xlsx
is the new format which is XML based. We are using apache POI to create excel files, when creating .xls
documents make sure the org.apache.poi:poi
dependency is on the classpath. When working with .xlsx
files, you need the org.apache.poi:poi-ooxml
dependency.
ExcelView
which extends from AbstractXlsView
. We create the excel document by overriding the buildExcelDocument, the rest is self explanatory.
public class ExcelView extends AbstractXlsView{
@Override
protected void buildExcelDocument(Map<String, Object> model,
Workbook workbook,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
// change the file name
response.setHeader("Content-Disposition", "attachment; filename=\"my-xls-file.xls\"");
@SuppressWarnings("unchecked")
List<User> users = (List<User>) model.get("users");
// create excel xls sheet
Sheet sheet = workbook.createSheet("User Detail");
sheet.setDefaultColumnWidth(30);
// create style for header cells
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName("Arial");
style.setFillForegroundColor(HSSFColor.BLUE.index);
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
font.setBold(true);
font.setColor(HSSFColor.WHITE.index);
style.setFont(font);
// create header row
Row header = sheet.createRow(0);
header.createCell(0).setCellValue("Firstname");
header.getCell(0).setCellStyle(style);
header.createCell(1).setCellValue("LastName");
header.getCell(1).setCellStyle(style);
header.createCell(2).setCellValue("Age");
header.getCell(2).setCellStyle(style);
header.createCell(3).setCellValue("Job Title");
header.getCell(3).setCellStyle(style);
header.createCell(4).setCellValue("Company");
header.getCell(4).setCellStyle(style);
header.createCell(5).setCellValue("Address");
header.getCell(5).setCellStyle(style);
header.createCell(6).setCellValue("City");
header.getCell(6).setCellStyle(style);
header.createCell(7).setCellValue("Country");
header.getCell(7).setCellStyle(style);
header.createCell(8).setCellValue("Phone Number");
header.getCell(8).setCellStyle(style);
int rowCount = 1;
for(User user : users){
Row userRow = sheet.createRow(rowCount++);
userRow.createCell(0).setCellValue(user.getFirstName());
userRow.createCell(1).setCellValue(user.getLastName());
userRow.createCell(2).setCellValue(user.getAge());
userRow.createCell(3).setCellValue(user.getJobTitle());
userRow.createCell(4).setCellValue(user.getCompany());
userRow.createCell(5).setCellValue(user.getAddress());
userRow.createCell(6).setCellValue(user.getCity());
userRow.createCell(7).setCellValue(user.getCountry());
userRow.createCell(8).setCellValue(user.getPhoneNumber());
}
}
}
Spring also provides 2 other abstract classes AbstractXlsxView
and AbstractXlsxStreamingView
to create xlsx files. When working with large excel documents it is profitable to use the streaming xlsx view. The streaming view uses less memory and can improve performance of large excel documents.
PDF view
For that we'll use iText library. Spring provides an AbstractPdfView abstract class which can be subclassed to create a helper class for generating PDF documents. However, it has a big drawback which the AbstractPdfView class only supports old API version of iText i.e. it is using the package com.lowagie.*
(iText version <= 2.1.7) while the recent iText’s package changes to com.itextpdf.*
(iText version >= 5.x)
The old iText version is no longer available nor supported, so subclassing AbstractPdfView
class is discouraged. Instead, I recommend to subclass the AbstractView class to create an iText 5.x-compatible version.
public abstract class AbstractPdfView extends AbstractView {
public AbstractPdfView() {
setContentType("application/pdf");
}
@Override
protected boolean generatesDownloadContent() {
return true;
}
@Override
protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// IE workaround: write into byte array first.
ByteArrayOutputStream baos = createTemporaryOutputStream();
// Apply preferences and build metadata.
Document document = new Document(PageSize.A4.rotate(), 36, 36, 54, 36);
PdfWriter writer = PdfWriter.getInstance(document, baos);
prepareWriter(model, writer, request);
buildPdfMetadata(model, document, request);
// Build PDF document.
document.open();
buildPdfDocument(model, document, writer, request, response);
document.close();
// Flush to HTTP response.
response.setHeader("Content-Disposition", "attachment"); // make browser to ask for download/display
writeToResponse(response, baos);
}
protected void prepareWriter(Map<String, Object> model, PdfWriter writer, HttpServletRequest request) throws DocumentException {
writer.setViewerPreferences(getViewerPreferences());
}
protected int getViewerPreferences() {
return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage;
}
protected void buildPdfMetadata(Map<String, Object> model, Document document, HttpServletRequest request) {
}
protected abstract void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer,
HttpServletRequest request, HttpServletResponse response) throws Exception;
}
After that, all what we need to do next is create PdfView
class and extend from the previously created AbstractPdfView
and override the buildPdfDocument(..)
method to create our PDF document.
CSV View
For that, we will follow the exact same approach used for PDF generation, which involves creating an abstract view class, a concrete view class and view resolver. I'm using Super CSV to generate csv files.
So, below the code for AbstractCsvView
that Subclass the Spring’s AbstractView
class:
public abstract class AbstractCsvView extends AbstractView {
private static final String CONTENT_TYPE = "text/csv";
private String url;
public AbstractCsvView() {
setContentType(CONTENT_TYPE);
}
public void setUrl(String url) {
this.url = url;
}
@Override
protected boolean generatesDownloadContent() {
return true;
}
@Override
protected final void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType(getContentType());
buildCsvDocument(model, request, response);
}
protected abstract void buildCsvDocument(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
Then, we write an implementation of the AbstractCsvView
class and make it implements the buildCsvDocument()
method as follows:
public class CsvView extends AbstractCsvView {
@Override
protected void buildCsvDocument(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setHeader("Content-Disposition", "attachment; filename=\"my-csv-file.csv\"");
List<User> users = (List<User>) model.get("users");
String[] header = {"Firstname","LastName","LastName","JobTitle","Company","Address","City","Country", "PhoneNumber"};
ICsvBeanWriter csvWriter = new CsvBeanWriter(response.getWriter(),
CsvPreference.STANDARD_PREFERENCE);
csvWriter.writeHeader(header);
for(User user : users){
csvWriter.write(user, header);
}
csvWriter.close();
}
}
Demo
Run the app, and to download:
- pdf file use: localhost:8080/download.pdf
- XLS file use: localhost:8080/download.xls
- CSV file use: localhost:8080/download.csv