Back to Spring Boot Course

Spring Boot Course

Let's create a Spring Boot REST API with file upload and download functionality, designed to upload employee details via Excel and generate PDFs and Excel files containing employee details upon request.

File Upload Excel Service: Employee Details

Our Employee File Upload Service accepts an Excel file containing all the details related to Employee, Visa, and Passport, and saves the data in an H2 database.

Let's add dependencies in pom.xml

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.3.0</version> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>

Lets include h2 db details in application.properties file.

spring.application.name=file-upload-download-service # H2 Database Configuration spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password spring.jpa.database-platform=org.hibernate.dialect.H2Dialect spring.jpa.hibernate.ddl-auto=update spring.h2.console.enabled=true

Let's create entities

Employee.java

package com.praveen.pdf.entity; import jakarta.persistence.*; @Entity public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="employee_id") private Long id; private String name; private String email; private String eidNumber; private String mobileNumber; private String gender; private String companyName; @OneToOne(mappedBy = "employee", cascade = CascadeType.ALL) private Passport passport; @OneToOne(mappedBy = "employee", cascade = CascadeType.ALL) private Visa visa; // getters and setters }

Visa.java

package com.praveen.pdf.entity; import jakarta.persistence.*; import java.time.LocalDate; @Entity public class Visa { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="visa_id") private Long id; private String visaStatus; private String emirates; private String visaType; private String residencyNumber; private LocalDate residencyExpiryDate; @OneToOne @JoinColumn(name = "employee_id") private Employee employee; // getters and setters }

Passport.java

package com.praveen.pdf.entity; import jakarta.persistence.*; import java.time.LocalDate; @Entity public class Passport { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="passport_id") private Long id; private String passportNumber; private String countryCode; private LocalDate issueDate; private LocalDate expiryDate; @OneToOne @JoinColumn(name = "employee_id") private Employee employee; // getters and setters }

Let's create repositories

EmployeeRepository.java

package com.praveen.pdf.repository; import com.praveen.pdf.entity.Employee; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface EmployeeRepository extends JpaRepository<Employee,Long> { }

PassportRepository.java

package com.praveen.file.repository; import com.praveen.file.entity.Passport; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface PassportRepository extends JpaRepository<Passport, Long> { }

VisaRepository.java

package com.praveen.file.repository; import com.praveen.file.entity.Visa; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface VisaRepository extends JpaRepository<Visa, Long> { }

Let's create service

package com.praveen.file.service; import com.praveen.file.entity.Employee; import com.praveen.file.entity.Passport; import com.praveen.file.entity.Visa; import com.praveen.file.repository.EmployeeRepository; import com.praveen.file.repository.PassportRepository; import com.praveen.file.repository.VisaRepository; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.time.LocalDate; import java.time.ZoneId; import java.util.Iterator; @Service public class FileUploadService { private final EmployeeRepository employeeRepository; private final PassportRepository passportRepository; private final VisaRepository visaRepository; public FileUploadService(EmployeeRepository employeeRepository, PassportRepository passportRepository, VisaRepository visaRepository) { this.employeeRepository = employeeRepository; this.passportRepository = passportRepository; this.visaRepository = visaRepository; } public void processEmployeeExcel(MultipartFile file) throws IOException { Workbook workbook = new XSSFWorkbook(file.getInputStream()); Sheet sheet = workbook.getSheetAt(0); Iterator<Row> rowIterator = sheet.iterator(); if (rowIterator.hasNext()) { rowIterator.next(); } while (rowIterator.hasNext()) { Row row = rowIterator.next(); Employee employee = new Employee(); employee.setName(row.getCell(0).getStringCellValue()); employee.setEmail(row.getCell(1).getStringCellValue()); employee.setEidNumber(row.getCell(2).getStringCellValue()); employee.setMobileNumber(row.getCell(3).getStringCellValue()); employee.setGender(row.getCell(4).getStringCellValue()); employee.setCompanyName(row.getCell(5).getStringCellValue()); Visa visa = new Visa(); visa.setVisaStatus(row.getCell(6).getStringCellValue()); visa.setEmirates(row.getCell(7).getStringCellValue()); visa.setVisaType(row.getCell(8).getStringCellValue()); visa.setResidencyNumber(row.getCell(9).getStringCellValue()); visa.setResidencyExpiryDate(convertToLocalDate(row.getCell(10).getDateCellValue())); Passport passport = new Passport(); passport.setPassportNumber(row.getCell(11).getStringCellValue()); passport.setCountryCode(row.getCell(12).getStringCellValue()); passport.setIssueDate(convertToLocalDate(row.getCell(13).getDateCellValue())); passport.setExpiryDate(convertToLocalDate(row.getCell(14).getDateCellValue())); visaRepository.save(visa); passportRepository.save(passport); employee.setVisa(visa); employee.setPassport(passport); employeeRepository.save(employee); passport.setEmployee(employee); visa.setEmployee(employee); visaRepository.save(visa); passportRepository.save(passport); } } private LocalDate convertToLocalDate(java.util.Date date) { if (date != null) { return date.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDate(); } return null; } }

Let's create controller

package com.praveen.file.controller; import com.praveen.file.service.FileUploadService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @Tag(name = "Employee File Upload Service", description = "Accepts an Excel file containing all the details related to Employee, Visa, and Passport, and saves the data in an H2 database.") public class FileUploadController { private final FileUploadService fileUploadService; public FileUploadController(FileUploadService fileUploadService) { this.fileUploadService = fileUploadService; } @Operation( summary = "Upload employee data from Excel file", description = "This endpoint allows the user to upload an Excel file containing employee details, including Visa and Passport information. The data is then saved in the H2 database.", tags = {"Employee File Upload Service"} ) @PostMapping(value = "/api/v1/employees/upload-excel", consumes = "multipart/form-data") public ResponseEntity<String> uploadEmployeeData( @Parameter(description = "The Excel file containing employee data, including Visa and Passport details.", required = true) @RequestParam("file") MultipartFile file) { try { fileUploadService.processEmployeeExcel(file); return ResponseEntity.ok("Data uploaded successfully!"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error processing the file."); } } }

Let's include swagger

package com.praveen.pdf.config; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class OpenApiConfig { @Bean OpenAPI customOpenAPI() { return new OpenAPI().components(new Components()) .info(new Info().title("PDF Generation Service").description("PDF Generation Service")); } }

Here with the sample excel to be uploaded.

Let's open swagger https://localhost:8080

screenshot of the app

Let's open h2 db to see whether data is inserted in the table.

screenshot of the app

File Download PDF Service: Employee Report PDF Generator

Our Employee Report PDF Generator is a feature that allows users to download detailed, customized PDF reports of employee information. By leveraging this service, users can request a PDF report for any employee, which will include essential details such as:

  • Basic Employee Information: Name, Email, EID Number, Mobile Number, Gender, and Company.
  • Visa Details (if applicable): Visa Status, Emirates, Visa Type, and Residency Number.
  • Passport Information (if available): Passport Number, Country Code, Issue Date, and Expiry Date.

The PDF file is generated on the fly based on the employee's unique ID, ensuring that the most up-to-date data is included in the report.

Let's add dependencies in pom.xml

<dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-core</artifactId> <version>9.0.0</version> <type>pom</type> </dependency>

Let's create service

package com.praveen.file.service; import com.itextpdf.kernel.colors.ColorConstants; import com.itextpdf.kernel.font.PdfFont; import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Cell; import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.element.Table; import com.itextpdf.layout.properties.TextAlignment; import com.itextpdf.layout.properties.UnitValue; import com.itextpdf.layout.properties.VerticalAlignment; import com.praveen.file.entity.Employee; import com.praveen.file.entity.Passport; import com.praveen.file.entity.Visa; import com.praveen.file.repository.EmployeeRepository; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import java.io.ByteArrayOutputStream; import java.io.IOException; @Service public class FileDownloadService { private final EmployeeRepository employeeRepository; public FileDownloadService(EmployeeRepository employeeRepository) { this.employeeRepository = employeeRepository; } public Resource generateEmployeePdf(Long id) throws IOException { Employee employee = employeeRepository.findById(id).orElse(null); try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(byteArrayOutputStream); PdfDocument pdf = new PdfDocument(writer); Document document = new Document(pdf); PdfFont boldFont = PdfFontFactory.createFont(com.itextpdf.io.font.constants.StandardFonts.HELVETICA_BOLD); PdfFont regularFont = PdfFontFactory.createFont(com.itextpdf.io.font.constants.StandardFonts.HELVETICA); Paragraph header = new Paragraph("Employee Report") .setFont(boldFont) .setFontSize(18) .setTextAlignment(TextAlignment.CENTER) .setFontColor(ColorConstants.BLACK) .setMarginBottom(20); document.add(header); if (employee != null) { Table employeeTable = new Table(UnitValue.createPercentArray(new float[]{25, 25, 25, 25})); employeeTable.setWidth(UnitValue.createPercentValue(100)); employeeTable.setBorder(null); addFieldValuePair(employeeTable, "Name:", employee.getName(), "Email:", employee.getEmail(), boldFont, regularFont); addFieldValuePair(employeeTable, "EID Number:", employee.getEidNumber(), "Mobile:", employee.getMobileNumber(), boldFont, regularFont); addFieldValuePair(employeeTable, "Gender:", employee.getGender(), "Company:", employee.getCompanyName(), boldFont, regularFont); Visa visa = employee.getVisa(); if (visa != null) { addSectionHeader(employeeTable, "Visa Information"); addFieldValuePair(employeeTable, "Visa Status:", visa.getVisaStatus(), "Emirates:", visa.getEmirates(), boldFont, regularFont); addFieldValuePair(employeeTable, "Visa Type:", visa.getVisaType(), "Residency Number:", visa.getResidencyNumber(), boldFont, regularFont); } Passport passport = employee.getPassport(); if (passport != null) { addSectionHeader(employeeTable, "Passport Information"); addFieldValuePair(employeeTable, "Passport Number:", passport.getPassportNumber(), "Country Code:", passport.getCountryCode(), boldFont, regularFont); addFieldValuePair(employeeTable, "Issue Date:", passport.getIssueDate().toString(), "Expiry Date:", passport.getExpiryDate().toString(), boldFont, regularFont); } document.add(employeeTable); } document.close(); return new ByteArrayResource(byteArrayOutputStream.toByteArray()); } } private Cell createCell(String content, PdfFont font, boolean isField) { Cell cell = new Cell(); Paragraph paragraph = new Paragraph(content) .setFont(font) .setTextAlignment(TextAlignment.LEFT) .setVerticalAlignment(VerticalAlignment.TOP) .setMarginTop(0) .setMarginBottom(0); cell.add(paragraph); if (isField) { cell.setBackgroundColor(ColorConstants.LIGHT_GRAY); } else { cell.setBackgroundColor(ColorConstants.WHITE); } return cell; } private void addSectionHeader(Table table, String header) throws IOException { Cell headerCell = new Cell(1, 4); headerCell.add(new Paragraph(header).setFont(PdfFontFactory.createFont(com.itextpdf.io.font.constants.StandardFonts.HELVETICA_BOLD))); headerCell.setBackgroundColor(ColorConstants.WHITE) .setTextAlignment(TextAlignment.LEFT) .setVerticalAlignment(VerticalAlignment.TOP) .setMarginTop(0) .setMarginBottom(0); table.addCell(headerCell); } private void addFieldValuePair(Table table, String field1, String value1, String field2, String value2, PdfFont boldFont, PdfFont regularFont) { table.addCell(createCell(field1, boldFont, true)); table.addCell(createCell(value1, regularFont, false)); table.addCell(createCell(field2, boldFont, true)); table.addCell(createCell(value2, regularFont, false)); } }

Let's create controller

package com.praveen.file.controller; import com.praveen.file.service.FileDownloadService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.time.LocalDate; @RestController @Tag(name = "Employee File Download Service", description = "Generates and downloads employee-related PDFs containing information about Employee, Visa, and Passport details.") public class FileDownloadController { private final FileDownloadService fileDownloadService; public FileDownloadController(FileDownloadService fileDownloadService) { this.fileDownloadService = fileDownloadService; } @Operation( summary = "Generate employee PDF", description = "Generates a PDF file containing the details of an employee, including Visa and Passport information, and returns the file for download.", tags = {"Employee File Download Service"} ) @GetMapping("/api/v1/employees/{id}/generate-pdf") public ResponseEntity<Resource> generatePdf( @Parameter(description = "The ID of the employee for whom the PDF is to be generated.", required = true) @PathVariable Long id) { try { Resource fileResource = fileDownloadService.generateEmployeePdf(id); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=employee_" + id + "_" + LocalDate.now().toString() + ".pdf") .contentType(MediaType.APPLICATION_PDF) .body(fileResource); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } }

Let's open swagger https://localhost:8080

screenshot of the app

Let's open pdf file

screenshot of the app

File Download Excel Service: Employee Details

Let's create an endpoint to download the excel with all the employees details.

FileDownloadController.java

package com.praveen.file.controller; import com.praveen.file.service.FileDownloadService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.time.LocalDate; @RestController @Tag(name = "Employee File Download Service", description = "Service for generating and downloading files (Excel) containing employee-related information, including details about employees, visas, and passports.") public class FileDownloadController { private final FileDownloadService fileDownloadService; public FileDownloadController(FileDownloadService fileDownloadService) { this.fileDownloadService = fileDownloadService; } @Operation( summary = "Download Employee Excel File", description = "Generates an Excel file containing the details of all employees, including Visa and Passport information, and returns the file for download.", tags = {"Employee File Download Service"} ) @GetMapping("/api/v1/employees/download-excel") public ResponseEntity<byte[]> downloadEmployeeExcel() throws IOException { byte[] excelData = fileDownloadService.generateEmployeeExcel(); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=employees.xlsx") .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(excelData); } }

Lets create a method in service class to fetch the data from H2 DB and prepare the excel.

package com.praveen.file.service; import com.praveen.file.entity.Employee; import com.praveen.file.entity.Passport; import com.praveen.file.entity.Visa; import com.praveen.file.repository.EmployeeRepository; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; @Service public class FileDownloadService { private final EmployeeRepository employeeRepository; public FileDownloadService(EmployeeRepository employeeRepository) { this.employeeRepository = employeeRepository; } public byte[] generateEmployeeExcel() throws IOException { List<Employee> employees = employeeRepository.findAll(); String[] headers = { "Name", "Email", "EID Number", "Mobile Number", "Gender", "Company Name", "Visa Status", "Emirates", "Visa Type", "Residency Number", "Residency Expiry Date", "Passport Number", "Country Code", "Issue Date", "Expiry Date" }; try (Workbook workbook = new XSSFWorkbook(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { Sheet sheet = workbook.createSheet("Employees"); CellStyle headerStyle = createHeaderStyle(workbook); CellStyle dataStyle = createDataStyle(workbook); createHeaderRow(sheet, headers, headerStyle); int rowIndex = 1; for (Employee employee : employees) { Row row = sheet.createRow(rowIndex++); fillEmployeeData(row, employee, dataStyle); } for (int i = 0; i < headers.length; i++) { sheet.autoSizeColumn(i); } workbook.write(byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } } private void createHeaderRow(Sheet sheet, String[] headers, CellStyle headerStyle) { Row headerRow = sheet.createRow(0); for (int i = 0; i < headers.length; i++) { org.apache.poi.ss.usermodel.Cell cell = headerRow.createCell(i); cell.setCellValue(headers[i]); cell.setCellStyle(headerStyle); } } private void fillEmployeeData(Row row, Employee employee, CellStyle dataStyle) { row.createCell(0).setCellValue(employee.getName()); row.createCell(1).setCellValue(employee.getEmail()); row.createCell(2).setCellValue(employee.getEidNumber()); row.createCell(3).setCellValue(employee.getMobileNumber()); row.createCell(4).setCellValue(employee.getGender()); row.createCell(5).setCellValue(employee.getCompanyName()); fillVisaData(row, employee.getVisa(), dataStyle); fillPassportData(row, employee.getPassport(), dataStyle); for (int i = 0; i < row.getPhysicalNumberOfCells(); i++) { row.getCell(i).setCellStyle(dataStyle); } } private void fillVisaData(Row row, Visa visa, CellStyle dataStyle) { row.createCell(6).setCellValue(visa != null ? visa.getVisaStatus() : ""); row.createCell(7).setCellValue(visa != null ? visa.getEmirates() : ""); row.createCell(8).setCellValue(visa != null ? visa.getVisaType() : ""); row.createCell(9).setCellValue(visa != null ? visa.getResidencyNumber() : ""); row.createCell(10).setCellValue(visa != null ? visa.getResidencyExpiryDate().toString() : ""); for (int i = 6; i <= 10; i++) { row.getCell(i).setCellStyle(dataStyle); } } private void fillPassportData(Row row, Passport passport, CellStyle dataStyle) { row.createCell(11).setCellValue(passport != null ? passport.getPassportNumber() : ""); row.createCell(12).setCellValue(passport != null ? passport.getCountryCode() : ""); row.createCell(13).setCellValue(passport != null ? passport.getIssueDate().toString() : ""); row.createCell(14).setCellValue(passport != null ? passport.getExpiryDate().toString() : ""); for (int i = 11; i <= 14; i++) { row.getCell(i).setCellStyle(dataStyle); } } private CellStyle createHeaderStyle(Workbook workbook) { CellStyle style = workbook.createCellStyle(); style.setFillForegroundColor(IndexedColors.GREY_80_PERCENT.getIndex()); style.setFillPattern(FillPatternType.SOLID_FOREGROUND); Font font = workbook.createFont(); font.setBold(true); font.setColor(IndexedColors.WHITE.getIndex()); style.setFont(font); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER); style.setBorderBottom(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN); style.setBorderLeft(BorderStyle.THIN); style.setBorderRight(BorderStyle.THIN); return style; } private CellStyle createDataStyle(Workbook workbook) { CellStyle style = workbook.createCellStyle(); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER); style.setWrapText(true); style.setBorderBottom(BorderStyle.THIN); style.setBorderTop(BorderStyle.THIN); style.setBorderLeft(BorderStyle.THIN); style.setBorderRight(BorderStyle.THIN); return style; } }

Let's open swagger https://localhost:8080

screenshot of the app

Let's open excel file

screenshot of the app