Upload CSV Files in REST API — Spring Boot + Kotlin + OpenCSV

1. Introduction

2. Prepare Test Data

The image presents the screenshot of the test data
The image presents the screenshot of the test data

3. Imports

implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.opencsv:opencsv:5.2")

4. Prepare Models

4.1. Create a User Class

data class User(
var id: Long? = null,
var firstName: String? = null,
var lastName: String? = null,
var email: String? = null,
var phoneNumber: String? = null,
var age: Int? = null
)

4.2. Create Custom Exceptions

@ResponseStatus(HttpStatus.BAD_REQUEST)
class BadRequestException(msg: String) : RuntimeException(msg)

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
class CsvImportException(msg: String) : RuntimeException(msg)

5. Implement CSV Service

@Service
class CsvService {
private fun throwIfFileEmpty(file: MultipartFile) {
if (file.isEmpty)
throw BadRequestException("Empty file")
}
}
private fun createCSVToBean(fileReader: BufferedReader?): CsvToBean<User> =
CsvToBeanBuilder<User>(fileReader)
.withType(User::class.java)
.withIgnoreLeadingWhiteSpace(true)
.build()
private fun closeFileReader(fileReader: BufferedReader?) {
try {
fileReader!!.close()
} catch (ex: IOException) {
throw CsvImportException("Error during csv import")
}
}
fun uploadCsvFile(file: MultipartFile): List {
throwIfFileEmpty(file)
var fileReader: BufferedReader? = null

try {
fileReader = BufferedReader(InputStreamReader(file.inputStream))
val csvToBean = createCSVToBean(fileReader)

return csvToBean.parse()
} catch (ex: Exception) {
throw CsvImportException("Error during csv import")
} finally {
closeFileReader(fileReader)
}
}

6. Configure CSV and Bean Binding

6.1. Bind With @CsvBindByName

data class User(
@CsvBindByName(column = "Id")
var id: Long? = null,
@CsvBindByName(column = "First Name")
var firstName: String? = null,
@CsvBindByName(column = "Last Name")
var lastName: String? = null,
@CsvBindByName(column = "Email")
var email: String? = null,
@CsvBindByName(column = "Phone number")
var phoneNumber: String? = null,
@CsvBindByName(column = "Age")
var age: Int? = null
)

6.2. Bind With @CsvBindByPosition

data class UserWithCsvBindByPosition(
@CsvBindByPosition(position = 0)
var id: Long? = null,
@CsvBindByPosition(position = 1)
var firstName: String? = null,
@CsvBindByPosition(position = 2)
var lastName: String? = null,
@CsvBindByPosition(position = 3)
var email: String? = null,
@CsvBindByPosition(position = 4)
var phoneNumber: String? = null,
@CsvBindByPosition(position = 5)
var age: Int? = null
)
private fun createCSVToBean(fileReader: BufferedReader?): CsvToBean =
CsvToBeanBuilder(fileReader)
.withType(User::class.java)
.withSkipLines(1)
.withIgnoreLeadingWhiteSpace(true)
.build()

7. Create REST Controller

@RestController
@RequestMapping("/api/csv")
class CsvController(
private val csvService: CsvService
) {
@PostMapping
fun uploadCsvFile(
@RequestParam("file") file: MultipartFile
): ResponseEntity<List> {
val importedEntries = csvService.uploadCsvFile(file)

return ResponseEntity.ok(importedEntries)
}
}

8. Validation

8.1. Testing Using Postman

The image shows the screenshot from Postman application testing.
The image shows the screenshot from Postman application testing.

8.2. POSTing CSV File with cURL

curl -X POST -F 'file=@users.csv' http://localhost:8080/api/csv

8. Summary

Hi, my name is Piotr and I am the founder of Codersee- a technical blog, where I am teaching programming by practical step by step tutorials.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store