This Spring tutorial provides some examples of implementing CSV file download functionality in a Spring MVC application. The CSV library to be used is Super CSV - an open source package that provides fast and powerful manipulation of CSV format. On the Spring side, we cover two approaches to build the CSV file download functionality:

  • Handling CSV generation and download in a separate controller class. This is the simplest and quickest way. The technique employed is similar to the one described in the tutorial: Spring MVC sample application for downloading files. The difference is that we use an external CSV library for generating CSV data from model data, before pushing the data to the client.
  • Subclassing the Spring’s AbstractView class to create a new CSV View class. This is more complex but flexible in terms of integration and reusability with Spring’s view resolvers. The technique is well described in the tutorial Spring Web MVC with PDF View Example (using iText 5.x).

Let’s look at the model data first. Suppose that we have the following model class (Book.java):

package net.codejava.spring.model;

public class Book {
	private String title;
	private String description;
	private String author;
	private String publisher;
	private String isbn;
	private String publishedDate;
	private float price;

	public Book() {
	}

	public Book(String title, String description, String author, String publisher,
			String isbn, String publishedDate, float price) {
		this.title = title;
		this.description = description;
		this.author = author;
		this.publisher = publisher;
		this.isbn = isbn;
		this.publishedDate = publishedDate;
		this.price = price;
	}

	// getters and setters...
}

Therefore, our purpose is to create a Spring MVC application that is able to generate a CSV file from a list of Books, and then push it back to the client for downloading. Now, let’s dive into each approach in details.

 

1. Approach #1: Coding a CSV File Download Controller

In this approach, we create a Spring controller class that handles everything like this:

package net.codejava.spring.controller;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import net.codejava.spring.model.Book;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.supercsv.io.CsvBeanWriter;
import org.supercsv.io.ICsvBeanWriter;
import org.supercsv.prefs.CsvPreference;

/**
 * This Spring controller class implements a CSV file download functionality.
 * @author www.codejava.net
 *
 */
@Controller
public class CSVFileDownloadController {
	@RequestMapping(value = "/downloadCSV")
	public void downloadCSV(HttpServletResponse response) throws IOException {

		String csvFileName = "books.csv";

		response.setContentType("text/csv");

		// creates mock data
		String headerKey = "Content-Disposition";
		String headerValue = String.format("attachment; filename=\"%s\"",
				csvFileName);
		response.setHeader(headerKey, headerValue);

		Book book1 = new Book("Effective Java", "Java Best Practices",
				"Joshua Bloch", "Addision-Wesley", "0321356683", "05/08/2008",
				38);

		Book book2 = new Book("Head First Java", "Java for Beginners",
				"Kathy Sierra & Bert Bates", "O'Reilly Media", "0321356683",
				"02/09/2005", 30);

		Book book3 = new Book("Thinking in Java", "Java Core In-depth",
				"Bruce Eckel", "Prentice Hall", "0131872486", "02/26/2006", 45);

		Book book4 = new Book("Java Generics and Collections",
				"Comprehensive guide to generics and collections",
				"Naftalin & Philip Wadler", "O'Reilly Media", "0596527756",
				"10/24/2006", 27);

		List<Book> listBooks = Arrays.asList(book1, book2, book3, book4);

		// uses the Super CSV API to generate CSV data from the model data 
		ICsvBeanWriter csvWriter = new CsvBeanWriter(response.getWriter(),
				CsvPreference.STANDARD_PREFERENCE);

		String[] header = { "Title", "Description", "Author", "Publisher",
				"isbn", "PublishedDate", "Price" };

		csvWriter.writeHeader(header);

		for (Book aBook : listBooks) {
			csvWriter.write(aBook, header);
		}

		csvWriter.close();
	}
}

As we can see, the downloadCSV() method handles requests with the URL: /downloadCSV. It creates some mock data (a list of Book objects), and then uses the Super CSV API to generate the CSV data. There are 3 important points here:

  • Setting the response’s content type to text/csv:
    response.setContentType("text/csv");
  • Wrapping the response’s writer object inside the CsvBeanWriter:
    ICsvBeanWriter csvWriter = new CsvBeanWriter(response.getWriter(), ...);
  • And finally closing the CSV writer in order to push the data to the client:
    csvWriter.close();

     

Recommended Book: Getting started with Spring Framework

 

2. Approach #2: Subclassing AbstractView class

This approach involves in creating an abstract view class, a concrete view class, configuring a view properties file and a view resolver. And finally, create a controller method.

Coding the abstract CSV view (AbstractCsvView.java):

Subclass the Spring’s AbstractView class by writing the following class:

package net.codejava.spring.view;

import java.io.IOException;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.view.AbstractView;
import org.supercsv.io.CsvBeanWriter;
import org.supercsv.io.ICsvBeanWriter;
import org.supercsv.prefs.CsvPreference;

/**
 * This class is an abstract CSV view which concrete views must implement. 
 * @author www.codejava.net
 *
 */
public abstract class AbstractCsvView extends AbstractView {
	private String fileName;

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	protected void prepareResponse(HttpServletRequest request,
			HttpServletResponse response) {
		String headerKey = "Content-Disposition";
		String headerValue = String.format("attachment; filename=\"%s\"",
				fileName);
		response.setContentType("text/csv");
		response.setHeader(headerKey, headerValue);
	}

	@Override
	protected void renderMergedOutputModel(Map<String, Object> model,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {

		ICsvBeanWriter csvWriter = new CsvBeanWriter(response.getWriter(),
				CsvPreference.STANDARD_PREFERENCE);

		buildCsvDocument(csvWriter, model);
		csvWriter.close();
	}

	/**
	 * The concrete view must implement this method. 
	 */
	protected abstract void buildCsvDocument(ICsvBeanWriter csvWriter,
			Map<String, Object> model) throws IOException;
}

This abstract class implements some skeleton code to write CSV data to the response, and leaves the task of building the CSV document to implementers by declaring the abstract method buildCsvDocument().

Coding the concrete CSV view (CsvViewImpl.java):

Write an implementation of the AbstractCsvView class and make it implements the buildCsvDocument() method as follows:

package net.codejava.spring.view;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import net.codejava.spring.model.Book;

import org.supercsv.io.ICsvBeanWriter;

/**
 * An implementation of the AbstractCsvView.
 * This decides what model data to be used to generate CSV data. 
 * @author www.codejava.net
 *
 */
public class CsvViewImpl extends AbstractCsvView {

	@Override
	protected void buildCsvDocument(ICsvBeanWriter csvWriter,
			Map<String, Object> model) throws IOException {

		List<Book> listBooks = (List<Book>) model.get("csvData");
		String[] header = (String[]) model.get("csvHeader");

		csvWriter.writeHeader(header);

		for (Book aBook : listBooks) {
			csvWriter.write(aBook, header);
		}
	}
}

As we can see, this concrete CSV view takes data from the model under the keys “csvData” and “csvHeader” and use the supplied CSV writer to generate the CSV data.

Configuring a view properties file (views.properties):

To use the CsvViewImpl class above, we need to create a properties file under the project’s classpath with the following content (views.properties):

ViewCSV.(class)=net.codejava.spring.view.CsvViewImpl
ViewCSV.fileName=JavaBooks.csv

Here, ViewCSV is the view name which will be resolved by a view resolver; the (class) attribute specifies the view implementation class and the fileName attribute specifies name of the CSV file to be sent to the client.

 

 

Configuring a view resolver:

If Java config is used, add the following method to your configuration class:

@Bean
public ViewResolver getCsvViewResolver(){

	ResourceBundleViewResolver resolver = new ResourceBundleViewResolver();
	resolver.setBasename("views");
	resolver.setOrder(1);

	return resolver;
}

That’s equivalent to the following XML descriptor (in case you are using XML configuration):

<bean id="csvViewResolver" 
		class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
	<property name="order" value="1"/>
	<property name="basename" value="views"/>
</bean>

Coding a controller method:

And finally, add the following method into your controller class:

@RequestMapping(value="/viewCSV")
public ModelAndView viewCSV(HttpServletResponse response) throws IOException {

	Book book1 = new Book("Effective Java", "Java Best Practices",
			"Joshua Bloch", "Addision-Wesley", "0321356683", "05/08/2008",
			38);

	Book book2 = new Book("Head First Java", "Java for Beginners",
			"Kathy Sierra & Bert Bates", "O'Reilly Media", "0321356683",
			"02/09/2005", 30);

	Book book3 = new Book("Thinking in Java", "Java Core In-depth",
			"Bruce Eckel", "Prentice Hall", "0131872486", "02/26/2006", 45);

	Book book4 = new Book("Java Generics and Collections",
			"Comprehensive guide to generics and collections",
			"Naftalin & Philip Wadler", "O'Reilly Media", "0596527756",
			"10/24/2006", 27);

	List<Book> listBooks = Arrays.asList(book1, book2, book3, book4);

	String[] header = { "Title", "Description", "Author", "Publisher",
			"isbn", "PublishedDate", "Price" };

	ModelAndView model = new ModelAndView("ViewCSV");
	model.addObject("csvData", listBooks);
	model.addObject("csvHeader", header);

	return model;
}

This method simply creates some mock data and passes it into the model. And the rest is handled by the view resolver and view class which we created previously.

 

3. Coding Test Home Page

To test both approaches, write the home.jsp page with the following content:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Spring MVC with CSV File Download Example</title>
    </head>
    <body>
    	<div align="center">
    	    <h1>Spring MVC with CSV File Download</h1>
	        <p><a href="/downloadCSV">Download CSV #1</a></p>
	        <p><a href="/viewCSV">Download CSV #2</a></p>
    	</div>
    </body>
</html>

This page simply displays two hyperlinks: the first one to download a CSV file using the first approach, and the second one uses the second approach.

 


4. Eclipse Project Structure and Maven Dependencies

For your reference, the following screenshot is the Maven project structure in Eclipse IDE:

Spring MVC CSV File Download Project Structure

To create this kind of project, you can follow this tutorial: Creating a Spring MVC project using Maven and Eclipse in one minute.

And update the pom.xml file for the latest releases of Java, Spring, Servlet API and Super CSV:

  • For Java 7 and Spring 3.2.5.RELEASE:
    <properties>
    	<java.version>1.7</java.version>
    	<spring.version>3.2.5.RELEASE</spring.version>
    	<cglib.version>2.2.2</cglib.version>
    </properties>
  • For Servlet 3.0 API:
    <dependency>
    	<groupId>javax.servlet</groupId>
    	<artifactId>javax.servlet-api</artifactId>
    	<version>3.0.1</version>
    	<scope>provided</scope>
    </dependency>
  • For Super CSV library:
    <dependency>
    	<groupId>net.sf.supercsv</groupId>
    	<artifactId>super-csv</artifactId>
    	<version>2.1.0</version>
    </dependency>


5. Testing the Application

Let’s deploy the application on a Servlet container like Tomcat, and type the following URL in browser:

http://localhost:8080/SpringMvcCSVFileDownloadExample

The default page gets displayed as follows:

Test Spring MVC CSV File Download App

Click on the first hyperlink, the browser will download the books.csv file, whereas the second link will cause the JavaBooks.csv to be downloaded. Here is content of the generated CSV file (some text is truncated for brevity):

Title,Description,Author,Publisher,isbn,PublishedDate,Price
Effective Java,Java Best Practices,Joshua Bloch,Addision-Wesley,0321356683,05/08/2008,38.0
Head First Java,Java for Beginners,Kathy Sierra...,O'Reilly Media,0321356683,02/09/2005,30.0
Thinking in Java,Java Core In-depth,Bruce Eckel,Prentice Hall,0131872486,02/26/2006,45.0
Java Generics...,Comprehensive guide...,Naftalin ...,O'Reilly Media,0596527756,10/24/2006,27.0

 

Recommended Book: Spring in Practice

Related Course: The Java Spring Tutorial

Submit to DeliciousSubmit to DiggSubmit to FacebookSubmit to Google BookmarksSubmit to StumbleuponSubmit to TechnoratiSubmit to TwitterSubmit to LinkedIn
Attachments:
Download this file (SpringMvcCSVFileDownloadExample.zip)SpringMvcCSVFileDownloadExample.zip[Eclipse-Maven project]30 kB