You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

KMM项目中如何在iOS端用Ktor上传30MB以上的视频?

KMM大视频流式上传解决方案

针对30MB+的大视频上传,核心思路是流式上传避免全量加载文件到内存,通过KMM的expect/actual机制封装平台特定的文件流处理,统一跨平台上传逻辑。

1. 跨平台文件封装(Common层)

先定义平台无关的文件抽象,通过expect声明类,两端分别实现:

// commonMain/kotlin/com/yourpackage/PlatformFile.kt
expect class PlatformFile(path: String) {
    fun asUploadPart(): PartData.FileItem
}

// 共用上传接口
suspend fun uploadVideo(
    file: PlatformFile,
    uploadUrl: String,
    contentType: ContentType = ContentType.Video.MP4
): Result<Unit> {
    return runCatching {
        val client = HttpClient(getPlatformClient())
        client.post(uploadUrl) {
            setBody(MultiPartFormDataContent(formData {
                append(
                    "video_file",
                    file.asUploadPart(),
                    headersOf(HttpHeaders.ContentType, contentType.toString())
                )
            }))
            // 添加其他必要请求头,比如Authorization
            headers {
                append(HttpHeaders.Authorization, "Bearer YOUR_TOKEN")
            }
        }
    }
}

expect fun getPlatformClient(): HttpClientEngine

2. Android端实现(AndroidMain)

Android直接用File和输入流转换为ByteReadChannel:

// androidMain/kotlin/com/yourpackage/PlatformFile.kt
actual class PlatformFile actual constructor(path: String) {
    private val file = File(path)

    actual fun asUploadPart(): PartData.FileItem {
        return PartData.FileItem(
            provider = { file.inputStream().toByteReadChannel() },
            headers = Headers.build {
                append(HttpHeaders.ContentDisposition, "filename=\"${file.name}\"")
            }
        )
    }
}

actual fun getPlatformClient(): HttpClientEngine = OkHttp.create()

3. iOS端实现(iOSMain)

iOS通过NSURL获取文件输入流,再转换为Kotlin协程的ByteReadChannel:

// iosMain/kotlin/com/yourpackage/PlatformFile.kt
import kotlinx.coroutines.io.reader
import kotlinx.coroutines.io.writeFully
import kotlinx.coroutines.io.ByteReadChannel
import platform.Foundation.NSFileHandle
import platform.Foundation.NSURL
import platform.Foundation.NSInputStream

actual class PlatformFile actual constructor(path: String) {
    private val fileUrl = NSURL.fileURLWithPath(path)

    actual fun asUploadPart(): PartData.FileItem {
        val inputStream = NSFileHandle.fileHandleForReadingFromURL(fileUrl, null)?.inputStream()
            ?: error("Failed to open file for reading: $path")

        return PartData.FileItem(
            provider = { inputStream.toByteReadChannel() },
            headers = Headers.build {
                append(HttpHeaders.ContentDisposition, "filename=\"${fileUrl.lastPathComponent}\"")
            }
        )
    }
}

// 扩展方法:将NSInputStream转换为ByteReadChannel
private fun NSInputStream.toByteReadChannel(): ByteReadChannel = reader {
    val buffer = ByteArray(8192) // 8KB缓冲区,可根据需求调整
    open()
    try {
        while (true) {
            val bytesRead = read(buffer, 0, buffer.size.toULong()).toInt()
            if (bytesRead <= 0) break
            channel.writeFully(buffer, 0, bytesRead)
        }
    } finally {
        close()
    }
}

actual fun getPlatformClient(): HttpClientEngine = Ios.create()

关键说明

  • 流式处理:两端均使用固定大小缓冲区读取文件,避免加载全量文件到内存,解决大文件OOM问题。
  • MultiPart上传:采用标准的表单上传格式,适配大多数后端接口,同时支持断点续传扩展(需后端配合)。
  • 权限处理:iOS端需确保文件路径在沙箱可访问范围内(如Documents目录),并在Info.plist中声明必要的文件访问权限。

内容的提问来源于stack exchange,提问作者Akshat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 04:13:13