A very simple File
API for Kotlin Multiplatform. It gets the job done.
For Jvm
, File
is typealias
to java.io.File
, and commonMain
extensions
point to kotlin.io
extensions so that the footprint is very small for
Java/Android only consumers.
The File
implementation for nonJvm
is operationally equivalent to
Jvm
for consistency across platforms. Please submit an issue
if you discover any inconsistencies (e.g. path resolution).
import io.matthewnelson.kmp.file.*
fun commonMain(f: File) {
// System directory separator character (`/` or `\`)
SysDirSep
// System temporary directory
SysTempDir
f.isAbsolute()
f.exists()
f.delete()
f.mkdir()
f.mkdirs()
// ↓↓ Extension functions ↓↓
f.name
f.parentPath
f.parentFile
f.path
f.absolutePath
f.absoluteFile
f.canonicalPath()
f.canonicalFile()
// equivalent to File("/some/path")
val file = "/some/path".toFile()
// resolve child paths
val child = file.resolve("child")
println(child.path) // >> `/some/path/child`
println(child.resolve(file).path) // >> `/some/path` (file is rooted)
// normalized File (e.g. removal of . and ..)
f.normalize()
// basic write functionality
f.writeBytes(ByteArray(25) { it.toByte() })
f.writeUtf8("Hello World!")
// basic read functionality
f.readBytes()
val utf8: String = f.readUtf8()
println(utf8) // prints >> Hello World!
}
import io.matthewnelson.kmp.file.*
fun nonJvmMain(f: File) {
// File permissions (no-op for Windows) for Node.js/Native
f.chmod("700")
}
import io.matthewnelson.kmp.file.*
fun jsMain(f1: File, f2: File) {
// Node.js specific extension functions
// Buffers for reading/writing
val buffer = f1.read()
val b = ByteArray(25) { i -> buffer.readInt8(i) }
println(b.toList())
// If APIs aren't available, simply unwrap
val bufferDynamic = buffer.unwrap()
val gzipBufferDynamic = try {
// zlib might not work if you haven't declared
// it, but for this example lets assume it's
// available.
js("require('zlib')").gzipSync(bufferDynamic)
} catch (t: Throwable) {
// helper for converting exceptions to IOException
throw t.toIOException()
}
// Can re-wrap the dynamic buffer for a "safer" API
val gzipBuffer = Buffer.wrap(gzipBufferDynamic)
f2.write(gzipBuffer)
// File stats
val lstats = f1.lstat()
val stats = f1.stat()
// If APIs aren't available, simply unwrap to use.
val statsDynamic = stats.unwrap()
statsDynamic.nlink as Int
}
import io.matthewnelson.kmp.file.*
@OptIn(DelicateFileApi::class, ExperimentalForeignApi::class)
fun nativeMain(f1: File, f2: File) {
// Native specific extension functions
// Use a CPointer<FILE> with auto-closure on completion
f1.fOpen(flags = "rb") { file1 ->
f2.fOpen(flags = "wb") { file2 ->
val buf = ByteArray(4096)
while (true) {
// posix.fread helper extension for CPointer<FILE>
val read = file1.fRead(buf)
if (read < 0) throw errnoToIOException(errno)
if (read == 0) break
// posix.fwrite helper extension for CPointer<FILE>
if (file2.fWrite(buf, len = read) < 0) {
// helper for converting an error to an IOException
throw errnoToIOException(errno)
}
}
}
}
}
dependencies {
implementation("io.matthewnelson.kmp-file:file:0.1.1")
}