본문 바로가기

Programming!

Kotlin Excel Reader/ fastexcel 코틀린으로 엑셀 읽기/쓰기

신규로 전달(?) 받은 프로젝트가 코틀린 환경으로 되어있어서.. 하...

여튼 공부나 해볼겸해서 코틀린에서 엑셀 읽기를 대충대충 만들어 봤다. 개발환경부터 실행까지..

어렵네..ㅎㅎ ( java 였으면 말이지 그냥 쑥..)

 

 

우선 플젝을 만들어보자. 아래 DB관련 부분은 제거해도 된다.

엑셀 라이브러리 추가만 확인하자. fastexcel . fastexcel-reader

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.4.5"
    id("io.spring.dependency-management") version "1.0.11.RELEASE"
    kotlin("jvm") version "1.4.32"
    kotlin("plugin.spring") version "1.4.32"
    kotlin("plugin.jpa") version "1.4.32"
}

group = "com.tistory.eclipse4j"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11

configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
    implementation("org.dhatim:fastexcel:0.12.3")
    implementation("org.dhatim:fastexcel-reader:0.12.3")

    runtimeOnly("mysql:mysql-connector-java")
    annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.projectreactor:reactor-test")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "11"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

 

테스트 코드를 하나만들어서 엑셀 읽기 메소드를 추가한다.

@Test
fun parallelStreams() {
    FileInputStream("excel-read-sample.xlsx").use { fis ->
        val wb = ReadableWorkbook(fis)
        val cellValues = wb.firstSheet.openStream().parallel()
            .map { row: Row ->
            	// cell 을 data class로 변환한다.
                rowCellsToDataClass(
                    row.stream() // parallel로 처리해도 된다.
                )
            }
            .collect(Collectors.toList())
        cellValues.forEach{o->println(o)}
    }
}

 

하나의 row에 cells를 특정 data class에 매핑하도록 한다.

// stream을 활용하든 다른 header 맵퍼를 사용하든 알아서 
private fun rowCellsToDataClass(stream: Stream<Cell>) : CellValue {
    val cells = stream.toList()
    return CellValue(cells[0].value.toString(), cells[1].value.toString(), cells[2].value.toString())
}

 

매핑될 data class

data class CellValue(var firstName: String, var secondName: String, var address : String)

 

실행결과

 

참고로 엑셀파일은 프로젝트 root path에 위치 시키도록 한다.

 

엑셀쓰기 

package com.tistory.eclipse4j.kotlin.excel

import org.dhatim.fastexcel.Workbook
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.function.Consumer


// https://github.com/dhatim/fastexcel
// https://github.com/Floern/casting-csv-kt
class ExcelWriterTest {

    @Throws(IOException::class)
    fun writeWorkbook(consumer: Consumer<Workbook?>) {
        val os = ByteArrayOutputStream(   )
        val wb = Workbook(os, "Test", "1.0")
        consumer.accept(wb)
        wb.finish()
        os.writeTo(FileOutputStream(File("excel-write-sample.xlsx")))
        //return os.toByteArray()

    }

    @Test
    @DisplayName("정상적인 엑셀 생성")
    fun parallelStreams() {
        writeWorkbook { wb ->
            val ws = wb!!.newWorksheet("Sheet 1")
            getDatas().withIndex().map {
                ws.value(it.index, 0, it.value.firstName)
                ws.value(it.index, 1, it.value.secondName)
                ws.value(it.index, 2, it.value.address)
                ws.value(it.index, 3, it.value.message)
            }

        }
    }

    fun getDatas() : List<WriteCellValue> {
        val writeCellValues: MutableList<WriteCellValue> = mutableListOf()
        writeCellValues.add(WriteCellValue("Gil1", "Grissom1", "Addr1", "우리집1"))
        writeCellValues.add(WriteCellValue("Gil2", "Grissom2", "Addr2", "우리집2"))
        return writeCellValues
    }

    data class WriteCellValue(var firstName: String, var secondName: String, var address : String, var message : String)
}