How to Return CSV Data in Browser From Spring Controller
Solution 1
You could write to the response directly using e.g.
@RequestMapping(value = "/api/foo.csv")
public void fooAsCSV(HttpServletResponse response) {
response.setContentType("text/plain; charset=utf-8");
response.getWriter().print("a,b,c\n1,2,3\n3,4,5");
}
Since the return type is void
and HttpServletResponse
is declared as a method argument the request is assumed to be completed when this method returns.
Solution 2
You can use the library supercsv.
<dependency>
<groupId>net.sf.supercsv</groupId>
<artifactId>super-csv</artifactId>
<version>2.1.0</version>
</dependency>
Here is how to use it:
1- define your model class that you want to write as csv:
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...
}
2- Do the following magic:
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
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.
*
*/
@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();
}
}
Solution 3
Have you tried @ResponseBody
on your controller method?
@RequestMapping(value = "/api/foo.csv")
@ResponseBody
public String fooAsCSV(HttpServletResponse response) {
response.setContentType("text/plain; charset=utf-8");
String data = "a,b,c\n1,2,3\n3,4,5";
return data;
}
Edit: Spring docs explain it here: http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-responsebody
Solution 4
here is a more detailed example for Bart's answer:
@GetMapping(value = "csv")
public void exportCsv(HttpServletResponse response) {
try {
// prepare response encoding and Headers
response.setContentType("text/csv");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setHeader("Content-Disposition", "attachment; filename=a.csv"); // specify the real file name users will get when download
try (Writer writer = new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8)) {
// writer.write('\uFEFF'); // BOM is essential in some scenary
writer.write("key,value\n");
List<Row> row = rowService.getAll();
for (Column column : row) {
writer.write(String.format("%d,%s)\n",column.getKey(), column.getValue()));
}
writer.flush(); // DONNOT forget to flush writer after everything done
} // writer will be closed automatically
} catch (IOException e) {
log.error("failed to export csv", e);
}
}
Solution 5
I had a similar task lately. Used ResponseEntity<byte[]> and the response header Content-Disposition. Like this:
@RequestMapping(value = "/api/foo.csv")
public ResponseEntity<byte[]> fooAsCSV() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add("Content-Type", "application/vnd.ms-excel");
responseHeaders.add("Content-Disposition", "attachment; filename=abc.csv");
String data = "a,b,c\n1,2,3\n3,4,5";
return new ResponseEntity<>(data.getBytes("ISO8859-15"), responseHeaders, HttpStatus.OK);
}
Related videos on Youtube
David Williams
Updated on May 20, 2021Comments
-
David Williams about 3 years
Let say I have CSV data in a string and want to return it from a Spring controller. Imagine the data looks like this
a,b,c 1,2,3 4,5,6
No matter what I have tried, the newlines come out as literally '\n' in the response content, and if I double escape them as in "\n", the response just contains the double backslashes too. In general, how to I return plain text data with newlines in it without the newlines being modified? I know how to return plain text, but still, the content comes with escaped newlines... This is what I current have (using Spring 3.0.5, not by choice)
@RequestMapping(value = "/api/foo.csv") public ResponseEntity<String> fooAsCSV() { HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.add("Content-Type", "text/plain; charset=utf-8"); String data = "a,b,c\n1,2,3\n3,4,5"; return new ResponseEntity<>(data, responseHeaders, HttpStatus.OK); }
Which produces literally the string
"a,b,c\n1,2,3\n,3,4,5"
In the browser. How do I make it produce the correct data with new lines in tact as shown above?
-
Marcel Stör about 10 yearsWhat if your method signature says
public String fooAsCSV()
with@RequestMapping(value = "/api/foo.csv", method = RequestMethod.GET, produces="text/plain")
and thenreturn "a,b,c\n1,2,3\n,3,4,5";
i.e. returning a string directly instead of usingResponseEntity
-
David Williams about 10 yearsUnfortunately that is only available in Spring 3.1+
-
Marcel Stör about 10 yearsOuch, missed the "Spring 3.0.5" bit, sorry.
-
David Williams about 10 yearsHey no worries, thanks for reading and taking the time. Still not sure how to do this, I found a slightly hacky way which is to set the content type to text/html and put <br/> instead of \n. That makes me feel dirty.
-
Bart about 10 yearsMaybe you can try to write a trivial message converter yourself. I'm not sure where the problem lies but I bet you could try and steal the
StringHttpMessageConverter
from another version of Spring and see what happens. I tested your method in Spring 4 and it works like expected. -
Alex R over 5 yearsPossible duplicate of How to make "Save As" window pop up when downloading files in Spring MVC?
-
-
David Williams about 10 yearsI did add the @ResponseBody annotation, but hey, writing to the output stream directly might do the trick. Let me try that.
-
Bart about 10 yearsI was commenting to @nickdos his answer and it came to me. Always funny to realize how simple the solution is.
-
nickdos about 10 yearscopy and paste error - return type now String. See docs.spring.io/spring/docs/3.0.x/spring-framework-reference/…
-
David Williams about 10 yearsTotally, I'm not sure if I was tired or what but this should have been way more obvious :)