diff --git a/filekit-core/src/webMain/kotlin/io/github/vinceglb/filekit/PlatformFile.web.kt b/filekit-core/src/webMain/kotlin/io/github/vinceglb/filekit/PlatformFile.web.kt index e2e74518..58f19428 100644 --- a/filekit-core/src/webMain/kotlin/io/github/vinceglb/filekit/PlatformFile.web.kt +++ b/filekit-core/src/webMain/kotlin/io/github/vinceglb/filekit/PlatformFile.web.kt @@ -12,7 +12,11 @@ import kotlinx.serialization.Serializable import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.Uint8Array import org.khronos.webgl.get +import org.khronos.webgl.set +import org.w3c.files.Blob +import org.w3c.files.FilePropertyBag import org.w3c.files.FileReader +import kotlin.collections.forEachIndexed import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.js.ExperimentalWasmJsInterop @@ -31,7 +35,22 @@ public actual data class PlatformFile( ) { public actual override fun toString(): String = path - public actual companion object + public actual companion object { + /** + * Creates a [PlatformFile] from a browser [Blob]. + * + * Since a browser [File] is a subtype of [Blob], it can also be passed directly. + */ + @OptIn(ExperimentalWasmJsInterop::class) + public operator fun invoke(blob: Blob, name: String): PlatformFile { + val file = BrowserFile( + fileBits = listOf(blob.unsafeCast()).toJsArray(), + fileName = name, + options = FilePropertyBag(type = blob.type), + ) + return PlatformFile(webFile = WebFile.FileWrapper(file = file)) + } + } } public actual val PlatformFile.name: String diff --git a/filekit-core/src/webTest/kotlin/io/github/vinceglb/filekit/PlatformFileWebTest.kt b/filekit-core/src/webTest/kotlin/io/github/vinceglb/filekit/PlatformFileWebTest.kt index db24a414..81167f3b 100644 --- a/filekit-core/src/webTest/kotlin/io/github/vinceglb/filekit/PlatformFileWebTest.kt +++ b/filekit-core/src/webTest/kotlin/io/github/vinceglb/filekit/PlatformFileWebTest.kt @@ -4,6 +4,8 @@ import io.github.vinceglb.filekit.exceptions.FileKitException import io.github.vinceglb.filekit.mimeType.MimeType import io.github.vinceglb.filekit.utils.createTestFile import kotlinx.coroutines.test.runTest +import org.w3c.files.Blob +import org.w3c.files.BlobPropertyBag import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -189,6 +191,50 @@ class PlatformFileWebTest { ) } + @OptIn(ExperimentalWasmJsInterop::class) + @Test + fun testPlatformFileFromBlob() = runTest { + val blob = Blob( + blobParts = listOf("Hello, World!".toJsString()).toJsArray(), + options = BlobPropertyBag(type = "text/plain"), + ) + + val file = PlatformFile(blob = blob, name = platformFile.name) + + assertEquals(platformFile.name, file.name) + assertEquals(platformFile.extension, file.extension) + assertEquals(platformFile.nameWithoutExtension, file.nameWithoutExtension) + assertEquals(platformFile.size(), file.size()) + + assertEquals( + platformFile.readBytes().decodeToString(), + file.readBytes().decodeToString(), + ) + assertEquals( + platformFile.readBytes().toList(), + file.readBytes().toList(), + ) + assertEquals(platformFile.mimeType(), file.mimeType()) + assertTrue(file.isRegularFile()) + assertFalse(file.isDirectory()) + } + + @OptIn(ExperimentalWasmJsInterop::class) + @Test + fun testPlatformFileFromEmptyBlob() = runTest { + val blob = Blob( + blobParts = emptyList().toJsArray(), + options = BlobPropertyBag(type = "application/octet-stream"), + ) + val file = PlatformFile(blob = blob, name = "empty.bin") + + assertEquals("empty.bin", file.name) + assertEquals(0L, file.size()) + assertTrue(file.readBytes().isEmpty()) + assertTrue(file.isRegularFile()) + assertFalse(file.isDirectory()) + } + private fun PlatformFile.webFileWrapper(): WebFile.FileWrapper = assertIs(webFile) }