@@ -13,8 +13,10 @@ import org.apache.poi.ss.usermodel.Sheet
1313import org.apache.poi.ss.usermodel.Workbook
1414import org.apache.poi.ss.usermodel.WorkbookFactory
1515import org.apache.poi.ss.util.CellReference
16+ import org.apache.poi.util.DefaultTempFileCreationStrategy
1617import org.apache.poi.util.LocaleUtil
1718import org.apache.poi.util.LocaleUtil.getUserTimeZone
19+ import org.apache.poi.util.TempFile
1820import org.apache.poi.xssf.usermodel.XSSFWorkbook
1921import org.jetbrains.kotlinx.dataframe.AnyFrame
2022import org.jetbrains.kotlinx.dataframe.AnyRow
@@ -31,6 +33,7 @@ import java.io.File
3133import java.io.InputStream
3234import java.io.OutputStream
3335import java.net.URL
36+ import java.nio.file.Files
3437import java.time.LocalDate
3538import java.time.LocalDateTime
3639import java.util.Calendar
@@ -55,6 +58,27 @@ public class Excel : SupportedDataFrameFormat {
5558internal class DefaultReadExcelMethod (path : String? ) : AbstractDefaultReadMethod(path, MethodArguments .EMPTY , readExcel)
5659
5760private const val readExcel = " readExcel"
61+ private const val readExcelTempFolderPrefix = " dataframe-excel"
62+
63+ /* *
64+ * To prevent [Issue #402](https://github.com/Kotlin/dataframe/issues/402):
65+ *
66+ * Creates new temp directory instead of the default `/tmp/poifiles` which would
67+ * cause permission issues for multiple users.
68+ */
69+ private fun setWorkbookTempDirectory () {
70+ val tempDir = try {
71+ Files .createTempDirectory(readExcelTempFolderPrefix)
72+ .toFile()
73+ .also { it.deleteOnExit() }
74+ } catch (e: Exception ) {
75+ // Ignore, let WorkbookFactory use the default temp directory instead
76+ return
77+ }
78+ TempFile .setTempFileCreationStrategy(
79+ DefaultTempFileCreationStrategy (tempDir)
80+ )
81+ }
5882
5983/* *
6084 * @param sheetName sheet to read. By default, the first sheet in the document
@@ -72,6 +96,7 @@ public fun DataFrame.Companion.readExcel(
7296 rowsCount : Int? = null,
7397 nameRepairStrategy : NameRepairStrategy = NameRepairStrategy .CHECK_UNIQUE ,
7498): AnyFrame {
99+ setWorkbookTempDirectory()
75100 val wb = WorkbookFactory .create(url.openStream())
76101 return wb.use { readExcel(wb, sheetName, skipRows, columns, rowsCount, nameRepairStrategy) }
77102}
@@ -92,6 +117,7 @@ public fun DataFrame.Companion.readExcel(
92117 rowsCount : Int? = null,
93118 nameRepairStrategy : NameRepairStrategy = NameRepairStrategy .CHECK_UNIQUE ,
94119): AnyFrame {
120+ setWorkbookTempDirectory()
95121 val wb = WorkbookFactory .create(file)
96122 return wb.use { readExcel(it, sheetName, skipRows, columns, rowsCount, nameRepairStrategy) }
97123}
@@ -129,6 +155,7 @@ public fun DataFrame.Companion.readExcel(
129155 rowsCount : Int? = null,
130156 nameRepairStrategy : NameRepairStrategy = NameRepairStrategy .CHECK_UNIQUE ,
131157): AnyFrame {
158+ setWorkbookTempDirectory()
132159 val wb = WorkbookFactory .create(inputStream)
133160 return wb.use { readExcel(it, sheetName, skipRows, columns, rowsCount, nameRepairStrategy) }
134161}
@@ -206,7 +233,8 @@ public fun DataFrame.Companion.readExcel(
206233 }
207234
208235 val name = repairNameIfRequired(nameFromCell, columnNameCounters, nameRepairStrategy)
209- columnNameCounters[nameFromCell] = columnNameCounters.getOrDefault(nameFromCell, 0 ) + 1 // increase the counter for specific column name
236+ columnNameCounters[nameFromCell] =
237+ columnNameCounters.getOrDefault(nameFromCell, 0 ) + 1 // increase the counter for specific column name
210238
211239 val values: List <Any ?> = valueRowsRange.map {
212240 val row: Row ? = sheet.getRow(it)
@@ -225,10 +253,17 @@ public fun DataFrame.Companion.readExcel(
225253 *
226254 * TODO: https://github.com/Kotlin/dataframe/issues/387
227255 */
228- private fun repairNameIfRequired (nameFromCell : String , columnNameCounters : MutableMap <String , Int >, nameRepairStrategy : NameRepairStrategy ): String {
256+ private fun repairNameIfRequired (
257+ nameFromCell : String ,
258+ columnNameCounters : MutableMap <String , Int >,
259+ nameRepairStrategy : NameRepairStrategy ,
260+ ): String {
229261 return when (nameRepairStrategy) {
230262 NameRepairStrategy .DO_NOTHING -> nameFromCell
231- NameRepairStrategy .CHECK_UNIQUE -> if (columnNameCounters.contains(nameFromCell)) throw DuplicateColumnNamesException (columnNameCounters.keys.toList()) else nameFromCell
263+ NameRepairStrategy .CHECK_UNIQUE -> if (columnNameCounters.contains(nameFromCell)) throw DuplicateColumnNamesException (
264+ columnNameCounters.keys.toList()
265+ ) else nameFromCell
266+
232267 NameRepairStrategy .MAKE_UNIQUE -> if (nameFromCell.isEmpty()) { // probably it's never empty because of filling empty column names earlier
233268 val emptyName = " Unknown column"
234269 if (columnNameCounters.contains(emptyName)) " ${emptyName}${columnNameCounters[emptyName]} "
0 commit comments