diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/FileCacheStrategy.common.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/FileCacheStrategy.common.kt
index f18ab7d6f..95a7dba1e 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/FileCacheStrategy.common.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/FileCacheStrategy.common.kt
@@ -1,8 +1,11 @@
 package net.mamoe.mirai.utils
 
+import kotlinx.io.core.Closeable
 import kotlinx.io.core.Input
+import kotlinx.io.core.use
 import kotlinx.io.errors.IOException
-import net.mamoe.mirai.utils.internal.InputStream
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
 
 /**
  * 缓存策略.
@@ -19,14 +22,6 @@ expect interface FileCacheStrategy {
     @Throws(IOException::class)
     fun newImageCache(input: Input): ExternalImage
 
-    /**
-     * 将 [input] 缓存为 [ExternalImage].
-     * 此函数应 close 这个 [InputStream]
-     */
-    @MiraiExperimentalAPI
-    @Throws(IOException::class)
-    fun newImageCache(input: InputStream): ExternalImage
-
     /**
      * 将 [input] 缓存为 [ExternalImage].
      * 此 [input] 的内容应是不变的.
@@ -49,12 +44,15 @@ expect interface FileCacheStrategy {
         @Throws(IOException::class)
         override fun newImageCache(input: Input): ExternalImage
 
-        @MiraiExperimentalAPI
-        @Throws(IOException::class)
-        override fun newImageCache(input: InputStream): ExternalImage
-
         @MiraiExperimentalAPI
         @Throws(IOException::class)
         override fun newImageCache(input: ByteArray): ExternalImage
     }
-}
\ No newline at end of file
+}
+
+internal inline fun <I : Closeable, O : Closeable, R> I.withOut(output: O, block: I.(output: O) -> R): R {
+    contract {
+        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
+    }
+    return use { output.use { block(this, output) } }
+}
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/ReusableInput.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/ReusableInput.kt
index 3f560243f..52c176589 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/ReusableInput.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/ReusableInput.kt
@@ -1,6 +1,7 @@
 package net.mamoe.mirai.utils.internal
 
 import io.ktor.utils.io.ByteWriteChannel
+import kotlinx.io.core.Input
 
 internal interface ReusableInput {
     val md5: ByteArray
@@ -8,4 +9,9 @@ internal interface ReusableInput {
 
     fun chunkedFlow(sizePerPacket: Int): ChunkedFlowSession<ChunkedInput>
     suspend fun writeTo(out: ByteWriteChannel): Long
+
+    /**
+     * Remember to close.
+     */
+    fun asInput(): Input
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/md5.common.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/md5.common.kt
index 6a30b73ac..1519fa8e8 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/md5.common.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/md5.common.kt
@@ -5,7 +5,7 @@ package net.mamoe.mirai.utils.internal
 import kotlinx.io.pool.DefaultPool
 import kotlinx.io.pool.ObjectPool
 
-expect abstract class InputStream {
+internal expect abstract class InputStream {
     open fun available(): Int
     open fun close()
     abstract fun read(): Int
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/FileCacheStrategy.jvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/FileCacheStrategy.jvm.kt
index 3b499c55c..2ddcb56d3 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/FileCacheStrategy.jvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/FileCacheStrategy.jvm.kt
@@ -2,10 +2,9 @@
 
 package net.mamoe.mirai.utils
 
-import kotlinx.io.core.Closeable
-import kotlinx.io.core.Input
-import kotlinx.io.core.readAvailable
-import kotlinx.io.core.readBytes
+import kotlinx.io.core.*
+import kotlinx.io.streams.asInput
+import kotlinx.io.streams.asOutput
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.utils.internal.InputStream
 import net.mamoe.mirai.utils.internal.asReusableInput
@@ -16,10 +15,11 @@ import java.io.IOException
 import java.io.OutputStream
 import java.net.URL
 import java.security.MessageDigest
+import java.util.*
 import javax.imageio.ImageIO
-import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
 import kotlin.contracts.contract
+import kotlin.io.use
 
 /**
  * 缓存策略.
@@ -44,7 +44,7 @@ actual interface FileCacheStrategy {
      */
     @MiraiExperimentalAPI
     @Throws(IOException::class)
-    actual fun newImageCache(input: InputStream): ExternalImage
+    fun newImageCache(input: InputStream): ExternalImage
 
     /**
      * 将 [input] 缓存为 [ExternalImage].
@@ -87,7 +87,7 @@ actual interface FileCacheStrategy {
 
         @MiraiExperimentalAPI
         @Throws(IOException::class)
-        actual override fun newImageCache(input: InputStream): ExternalImage {
+        override fun newImageCache(input: InputStream): ExternalImage {
             return newImageCache(input.readBytes())
         }
 
@@ -186,12 +186,6 @@ actual interface FileCacheStrategy {
     }
 }
 
-internal inline fun <I : Closeable, O : Closeable, R> I.withOut(output: O, block: I.(output: O) -> R): R {
-    contract {
-        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
-    }
-    return use { output.use { block(this, output) } }
-}
 
 @Throws(IOException::class)
 internal fun Input.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SIZE): Long {
@@ -204,4 +198,4 @@ internal fun Input.copyTo(out: OutputStream, bufferSize: Int = DEFAULT_BUFFER_SI
         bytes = readAvailable(buffer)
     }
     return bytesCopied
-}
+}
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/asReusableInput.jvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/asReusableInput.jvm.kt
index 168b043f8..349ccac39 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/asReusableInput.jvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/asReusableInput.jvm.kt
@@ -4,6 +4,9 @@ import io.ktor.utils.io.ByteWriteChannel
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.withContext
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.Input
+import kotlinx.io.streams.asInput
 import net.mamoe.mirai.message.data.toLongUnsigned
 import java.io.File
 import java.io.InputStream
@@ -28,6 +31,10 @@ internal actual fun ByteArray.asReusableInput(): ReusableInput {
             out.flush()
             return this@asReusableInput.size.toLongUnsigned()
         }
+
+        override fun asInput(): Input {
+            return ByteReadPacket(this@asReusableInput)
+        }
     }
 }
 
@@ -50,6 +57,10 @@ internal fun File.asReusableInput(deleteOnClose: Boolean): ReusableInput {
         override suspend fun writeTo(out: ByteWriteChannel): Long {
             return inputStream().use { it.copyTo(out) }
         }
+
+        override fun asInput(): Input {
+            return inputStream().asInput()
+        }
     }
 }
 
@@ -72,6 +83,10 @@ internal fun File.asReusableInput(deleteOnClose: Boolean, md5: ByteArray): Reusa
         override suspend fun writeTo(out: ByteWriteChannel): Long {
             return inputStream().use { it.copyTo(out) }
         }
+
+        override fun asInput(): Input {
+            return inputStream().asInput()
+        }
     }
 }
 
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/md5.jvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/md5.jvm.kt
index 3e997fb4b..3b08a5a90 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/md5.jvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/md5.jvm.kt
@@ -19,4 +19,5 @@ internal actual fun InputStream.md5(): ByteArray {
     return digest.digest()
 }
 
+@Suppress("ACTUAL_WITHOUT_EXPECT")
 internal actual typealias InputStream = java.io.InputStream
\ No newline at end of file