
Embed static resources into binaries via compile-time Base64 encoding with generated ResourceDirectory, lazy decoding, smart in-memory/disk caching, integrity validation, and configurable optimization strategies.
Embed static resources directly into your Kotlin binaries
KtEmbed is a Kotlin Multiplatform library and Gradle plugin that lets you embed static resources (text, JSON, HTML, images, etc.) directly into your compiled applications. Resources are encoded as Z85 strings at compile time and can be accessed efficiently at runtime with automatic caching and optimization strategies.
Add the KtEmbed plugin to your build.gradle.kts:
plugins {
id("dev.ktool.ktembed") version "0.0.1"
}ktembed {
packageName = "com.example.resources"
resourceDirectories = listOf("src/main/resources")
}import com.example.resources.ResourceDirectory
// Initialize the Resources instance
val resources = ResourceDirectory.toResources()
// Read as string
val config = resources.asString("config/config.json")
// Read as bytes
val image = resources.asBytes("img/logo.png")
// Check if resource exists
if (resources.exists("text/optional.txt")) {
println(resources.asString("text/optional.txt"))
}
// Write to output stream (JVM only)
FileOutputStream("output.png").use { out ->
resources.write("logo.png", out)
}
// Get file path (cached to temp directory)
val filePath = resources.asPath("data.bin")// In settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
}
}
// In build.gradle.kts
plugins {
id("dev.ktool.ktembed") version "0.1.1"
}
dependencies {
implementation("dev.ktool:ktembed-runtime:0.1.1")
}The ktembed extension provides the following configuration options:
| Property | Type | Required | Description |
|---|---|---|---|
packageName |
String |
Yes | Package name for the generated ResourceDirectory class |
resourceDirectories |
StringList |
Yes | Directories to scan for resources |
exclude |
(String) -> Boolean |
No | Function to exclude paths (returns true to ignore) |
ktembed {
// Package for generated code
packageName = "com.myapp.assets"
// Multiple resource directories
resourceDirectories = listOf(
"src/main/resources",
"assets",
"static"
)
// Filter function to exclude files
exclude = { path ->
path.endsWith(".tmp") || // Ignore temp files
path.contains(".git") || // Ignore git files
path.startsWith("private/") // Ignore private directory
}
}The main API for accessing embedded resources.
Check if a resource exists at the given path.
if (resources.exists("config.json")) {
// Resource exists
}Read resource as UTF-8 string. Result is cached in memory.
val json = resources.asString("data.json")Read resource as bytes. Result is cached in memory.
val imageData = resources.asByteArray("image.png")Get a file system path to the resource. The resource is extracted to a cache directory and validated.
val configFile = resources.asPath("config.properties")
// Use with libraries that need file pathsWrite resource to an Okio Sink. Automatically chooses optimization strategy based on inMemoryCutoff.
FileSystem.SYSTEM.sink(outputPath).use { sink ->
resources.write("large-file.bin", sink)
}Write resource with explicit optimization strategy.
resources.write("data.bin", sink, OptimizationStrategy.Memory)Write resource to a Java OutputStream.
FileOutputStream("output.txt").use { out ->
resources.write("input.txt", out)
}Choose between speed and memory efficiency:
enum class OptimizationStrategy {
Speed, // Load entire resource into memory (faster)
Memory // Stream from disk cache (uses less memory)
}When to use:
Speed: Small resources, frequent access, plenty of memoryMemory: Large resources (>6MB), infrequent access, memory-constrained environmentsResources class and are lazily decoded and cached as neededByteArray and String values are cached per Resource instanceResources extracted to disk are automatically validated:
val filePath = resources.asPath("important.dat")
// File is validated by comparing hashes
// If validation fails, the file is regeneratedwrite() for large files instead of asBytes() to avoid loading into memoryinMemoryCutoff based on your application's memory constraintsOptimizationStrategy.Memory for resources >4MBResources instance - don't create multiple instances of the same ResourceDirectory
Ensure the generateKtEmbedResources task runs before compilation:
tasks.named("compileKotlin") {
dependsOn("generateKtEmbedResources")
}This is handled automatically by the plugin, but can be added explicitly if needed.
Reduce the inMemoryCutoff value:
val resources = Resources(ResourceDirectory(), inMemoryCutoff = 1_000_000) // 1MBOr use OptimizationStrategy.Memory for large resources:
resources.write("large-file.bin", output, OptimizationStrategy.Memory)The filter function returns true for paths to ignore:
// Correct: ignore .tmp files
filter = { path -> path.endsWith(".tmp") }
// Incorrect: this would ignore everything EXCEPT .tmp files
filter = { path -> !path.endsWith(".tmp") }Embed static resources directly into your Kotlin binaries
KtEmbed is a Kotlin Multiplatform library and Gradle plugin that lets you embed static resources (text, JSON, HTML, images, etc.) directly into your compiled applications. Resources are encoded as Z85 strings at compile time and can be accessed efficiently at runtime with automatic caching and optimization strategies.
Add the KtEmbed plugin to your build.gradle.kts:
plugins {
id("dev.ktool.ktembed") version "0.0.1"
}ktembed {
packageName = "com.example.resources"
resourceDirectories = listOf("src/main/resources")
}import com.example.resources.ResourceDirectory
// Initialize the Resources instance
val resources = ResourceDirectory.toResources()
// Read as string
val config = resources.asString("config/config.json")
// Read as bytes
val image = resources.asBytes("img/logo.png")
// Check if resource exists
if (resources.exists("text/optional.txt")) {
println(resources.asString("text/optional.txt"))
}
// Write to output stream (JVM only)
FileOutputStream("output.png").use { out ->
resources.write("logo.png", out)
}
// Get file path (cached to temp directory)
val filePath = resources.asPath("data.bin")// In settings.gradle.kts
pluginManagement {
repositories {
gradlePluginPortal()
}
}
// In build.gradle.kts
plugins {
id("dev.ktool.ktembed") version "0.1.1"
}
dependencies {
implementation("dev.ktool:ktembed-runtime:0.1.1")
}The ktembed extension provides the following configuration options:
| Property | Type | Required | Description |
|---|---|---|---|
packageName |
String |
Yes | Package name for the generated ResourceDirectory class |
resourceDirectories |
StringList |
Yes | Directories to scan for resources |
exclude |
(String) -> Boolean |
No | Function to exclude paths (returns true to ignore) |
ktembed {
// Package for generated code
packageName = "com.myapp.assets"
// Multiple resource directories
resourceDirectories = listOf(
"src/main/resources",
"assets",
"static"
)
// Filter function to exclude files
exclude = { path ->
path.endsWith(".tmp") || // Ignore temp files
path.contains(".git") || // Ignore git files
path.startsWith("private/") // Ignore private directory
}
}The main API for accessing embedded resources.
Check if a resource exists at the given path.
if (resources.exists("config.json")) {
// Resource exists
}Read resource as UTF-8 string. Result is cached in memory.
val json = resources.asString("data.json")Read resource as bytes. Result is cached in memory.
val imageData = resources.asByteArray("image.png")Get a file system path to the resource. The resource is extracted to a cache directory and validated.
val configFile = resources.asPath("config.properties")
// Use with libraries that need file pathsWrite resource to an Okio Sink. Automatically chooses optimization strategy based on inMemoryCutoff.
FileSystem.SYSTEM.sink(outputPath).use { sink ->
resources.write("large-file.bin", sink)
}Write resource with explicit optimization strategy.
resources.write("data.bin", sink, OptimizationStrategy.Memory)Write resource to a Java OutputStream.
FileOutputStream("output.txt").use { out ->
resources.write("input.txt", out)
}Choose between speed and memory efficiency:
enum class OptimizationStrategy {
Speed, // Load entire resource into memory (faster)
Memory // Stream from disk cache (uses less memory)
}When to use:
Speed: Small resources, frequent access, plenty of memoryMemory: Large resources (>6MB), infrequent access, memory-constrained environmentsResources class and are lazily decoded and cached as neededByteArray and String values are cached per Resource instanceResources extracted to disk are automatically validated:
val filePath = resources.asPath("important.dat")
// File is validated by comparing hashes
// If validation fails, the file is regeneratedwrite() for large files instead of asBytes() to avoid loading into memoryinMemoryCutoff based on your application's memory constraintsOptimizationStrategy.Memory for resources >4MBResources instance - don't create multiple instances of the same ResourceDirectory
Ensure the generateKtEmbedResources task runs before compilation:
tasks.named("compileKotlin") {
dependsOn("generateKtEmbedResources")
}This is handled automatically by the plugin, but can be added explicitly if needed.
Reduce the inMemoryCutoff value:
val resources = Resources(ResourceDirectory(), inMemoryCutoff = 1_000_000) // 1MBOr use OptimizationStrategy.Memory for large resources:
resources.write("large-file.bin", output, OptimizationStrategy.Memory)The filter function returns true for paths to ignore:
// Correct: ignore .tmp files
filter = { path -> path.endsWith(".tmp") }
// Incorrect: this would ignore everything EXCEPT .tmp files
filter = { path -> !path.endsWith(".tmp") }