Spring: How to import data via CSV

In this article, I want to show you how you can import data via a CSV file in a cloud based application.

Spring controller

In order to upload the CSV file, we will use an HTTP POST request with mime type as multipart/form-data.

This request will be processed by my spring rest controller.

I will get the file via the request parameter with type : MultipartFile.

If the file content is not empty, I can extract the bytes and encode them as Base64 encoding using java 8 utility.

In order to do the work outside of a user-facing request, I use the Google Cloud Platform (GCP) task queues mechanism.

I can pass the encoded content as a task parameter. In parallel to the user facing request, a servlet will do the job with the specified parameter.

@RestController
@RequestMapping("/assessment-equipments")
public class AssessmentEquipmentController {
private final AssessmentEquipmentService assessmentEquipmentService;
private final AssessmentMeasureService assessmentMeasureService;
public AssessmentEquipmentController(AssessmentEquipmentService assessmentEquipmentService,
AssessmentMeasureService assessmentMeasureService) {
this.assessmentEquipmentService = assessmentEquipmentService;
this.assessmentMeasureService = assessmentMeasureService;
}
@PostMapping("/{id}/uncertainties")
public ResponseEntity<String> saveUncertainties(@NotNull @PathVariable Long id,
@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return new ResponseEntity<>("Please select a file!", HttpStatus.OK);
}
try {
byte[] content = file.getBytes();
Queue queue = QueueFactory.getDefaultQueue();
queue.add(TaskOptions.Builder.withUrl("/admin/assessment-measures")
.param("id", String.valueOf(id))
.param("csv", Base64.getEncoder().encodeToString(content)));
return new ResponseEntity<>("The CSV file for the assessment equipment with ID [" + id + "] will be processed soon.", HttpStatus.OK);
} catch (IOException e) {
return new ResponseEntity<>("Oops something goes wrong!", HttpStatus.CONFLICT);
}
}
}

Servlet Consumer

The servlet is pretty straightforward :

  1. It extracts the encoded content
  2. It decode the encoded content
  3. It loads object list from the CSV content (jackson csv)
  4. It calls a service with the list of objects 
@WebServlet(
name = "AssessmentMeasureImporter",
description = "Import CSV file for assessment measure",
urlPatterns = "/admin/assessment-measures"
)
public class AssessmentMeasureImporter extends HttpServlet {
@Autowired
private AssessmentMeasureService assessmentMeasureService;
public void init(ServletConfig config) throws ServletException {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
config.getServletContext());
}
public void doPost(HttpServletRequest request, HttpServletResponse response) {
Long id = ServletUtils.getLongParameter(request, "id");
byte[] content = ServletUtils.getBytesParameter(request, "csv");
List<AssessmentMeasureDto> assessmentMeasures = CsvUtils
.loadObjectList(AssessmentMeasureDto.class, content);
assessmentMeasureService.saveByAssessmentEquipment(id, assessmentMeasures);
response.setStatus(200);
}
}

CSV Utility

In the servlet, I've called a utility method for reading the CSV content into an array of Objects.
I use for this purpose the Jackson library.

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-csv</artifactId>
<version>2.9.9</version>
</dependency>
public static <T> List<T> loadObjectList(Class<T> type, byte[] content) {
Assert.notNull(type,"type must not be null");
Assert.notNull(content,"content must not be null");
try {
CsvSchema schema = CsvSchema.emptySchema().withHeader();
CsvMapper mapper = new CsvMapper();
MappingIterator<T> readValues = mapper.readerFor(type)
.with(schema).readValues(content);
return readValues.readAll();
} catch (Exception e) {
return Collections.emptyList();
}
}

Happy learning... 


Comments

Popular posts from this blog

Spring JPA : Using Specification with Projection

Chip input using Reactive Form