Importing data from CSV (Comma-Separated Values) files is a basic feature offered by many different services. At the same time, CSV is one of the most popular formats in data science.
Although dealing with CSV content is common, the approach I used to follow was to convert the data returned by an API or a query into a valid CSV file. This involves boilerplate code and unnecessary overhead.
So, producing CSV as output, allowing users to download it, is a more practical, advanced, and automatable solution.
Let's see how to build an API returning CSV content in Spring Boot.
1. Adding the Required Dependencies
There are many libraries to deal with CSVs in Java, but I strongly recommend Apache Commons CSV. First of all, you need to add it to your project's dependencies.
If you are a Gradle user, add this dependency to your project's build file:
compile "org.apache.commons:commons-csv:1.8"
Otherwise, if you are a Maven user, add the following dependency to your project's build POM:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.8</version>
</dependency>
Now, you have all you need to produce or read CSV content in Spring Boot.
2. Defining the API
Before showing how to achieve the result, I want to delve into a few details. In the HTTP protocol, the Content-Type
header is used to indicate the media type of the returned content.
As described here, the recommended media type for an HTTP request that returns CSV is text/csv
. Please, note that the media types offered by Spring Boot through the MediaType class do not include it. Since you cannot use a predefined value proposed by the framework, you will have to custom define it.
Another vital notion to clarify is that CSV files are often very different from each other. You may need to produce files with different delimiters, enclosing quotes, escaping characters, line separators, encodings, etc. Even though this is not what this article is aimed at, you should know that Apache Commons CSV allows you to choose between multiple predefined formats or define your own through the CSVFormat
class.
Now, let's see what an API returning CSV in Spring Boot looks like:
Java
@GetMapping(value = "/exportCSV", produces = "text/csv")
public ResponseEntity<Resource> exportCSV() {
// replace this with your header (if required)
String[] csvHeader = {
"name", "surname", "age"
};
// replace this with your data retrieving logic
List<List<String>> csvBody = new ArrayList<>();
csvBody.add(Arrays.asList("Patricia", "Williams", "25"));
csvBody.add(Arrays.asList("John", "Smith", "44"));
csvBody.add(Arrays.asList("Douglas", "Brown", "31"));
ByteArrayInputStream byteArrayOutputStream;
// closing resources by using a try with resources
// https://www.baeldung.com/java-try-with-resources
try (
ByteArrayOutputStream out = new ByteArrayOutputStream();
// defining the CSV printer
CSVPrinter csvPrinter = new CSVPrinter(
new PrintWriter(out),
// withHeader is optional
CSVFormat.DEFAULT.withHeader(csvHeader)
);
) {
// populating the CSV content
for (List<String> record : csvBody)
csvPrinter.printRecord(record);
// writing the underlying stream
csvPrinter.flush();
byteArrayOutputStream = new ByteArrayInputStream(out.toByteArray());
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
InputStreamResource fileInputStream = new InputStreamResource(byteArrayOutputStream);
String csvFileName = "people.csv";
// setting HTTP headers
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + csvFileName);
// defining the custom Content-Type
headers.set(HttpHeaders.CONTENT_TYPE, "text/csv");
return new ResponseEntity<>(
fileInputStream,
headers,
HttpStatus.OK
);
}
Kotlin
@GetMapping(value = ["/exportCSV"], produces = ["text/csv"])
fun exportCSV() : ResponseEntity<Resource> {
// replace this with your header (if required)
val csvHeader = arrayOf(
"name", "surname", "age"
)
// replace this with your data retrieving logic
val csvBody = ArrayList<List<String>>()
csvBody.add(listOf("Patricia", "Williams", "25"))
csvBody.add(listOf("John", "Smith", "44"))
csvBody.add(listOf("Douglas", "Brown", "31"))
val byteArrayOutputStream =
ByteArrayOutputStream()
.use { out ->
// defining the CSV printer
CSVPrinter(
PrintWriter(out),
// withHeader is optional
CSVFormat.DEFAULT.withHeader(*csvHeader)
)
.use { csvPrinter ->
// populating the CSV content
csvBody.forEach { record ->
csvPrinter.printRecord(record)
}
// writing the underlying stream
csvPrinter.flush()
ByteArrayInputStream(out.toByteArray())
}
}
val fileInputStream = InputStreamResource(byteArrayOutputStream)
val csvFileName = "people.csv"
// setting HTTP headers
val headers = HttpHeaders()
headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=${csvFileName}")
// defining the custom Content-Type
headers.set(HttpHeaders.CONTENT_TYPE, "text/csv")
return ResponseEntity(
fileInputStream,
headers,
HttpStatus.OK
)
}
First, a ByteArrayOutputStream
object is initialized and passed to a CSVPrinter
object. The latter is used to append the values of each record to the stream through the printRecord
method. Then, the stream is written by calling the flush()
method. Finally, the stream is wrapped in an InputStreamResource
object and returned by the API using custom-defined HTTP headers.
Note that in the example I decided to add a header to the CSV by calling withHeader()
while creating the CSVPrinter
object. This is not mandatory and depends on what you want your output content to look like.
3. The API in Action
This is what an example of a response from such an API looks like:
name,surname,age
Patricia,Williams,25
John,Smith,44
Douglas,Brown,31
As you can see, the output generated by the API is valid CSV content that can be easily downloaded by users or passed to other systems.
Conclusion
Returning CSV content from an API is much more efficient than converting its output accordingly. Here we looked at how to achieve such a result in Spring Boot in Java or Kotlin.
I hope that you found this article helpful, thanks for reading!
The post "Returning CSV Content From an API in Spring Boot" appeared first on Writech.
Top comments (0)