互联网科技码农的世界程序员技术栈

[Kotlin/Native] 踩坑,Ktor-Client 如

2019-06-18  本文已影响15人  何晓杰Dev

首先给大家一段官方文档里的代码,来看看用 Jtor-Client 如何上传文件:

官方文档

可以注意到这里有一个关键的代码是 inputStream.asInput(),这个代码在 JVM 的 Target 下很容易构造,我们只需要使用 FileInputStream 就万事大吉了,复杂点的可以写成这样:

private fun buildPostFileBody(params: Map<String, String>, files: Map<String, String>) = MultiPartFormDataContent(
    formData {
        params.forEach { (t, u) -> append(t, u) }
        files.forEach { (t, u) ->
            appendInput(t, headersOf("Content-Type", "application/octet-stream")) {
                FileInputStream(File(u)).asInput()
            }
        }
    }
)

然鹅在 Kotlin/Native 下,没有 FileStream 了,也就没有这样简单的 asInput() 的操作。一开始我也进行了搜索,没找到相应的资料,相反的也找到有一些人,甚至是老外说,Ktor-Client 在 Native 下是有缺陷的,比如说不能上传文件。

当然我并不会认同这些,没有资料那就要自己研究了,在上一篇文章里(点击阅读),我讲了如何在 K/N 下包装出形同 JVM 的 File 类,我们的解决方案也是由此而来的。

下面简单说一下思路,首先 Input 是一个接口,所以我们必然要找 Input 的实现类,然后再去看实现类里有没有什么可以用的东西。

搜索结果

我们可以瞬间锁定 ByteReadPacketIoBuffer 这两个类,至于中间的那个是抽象类,应该不是我们要的,那就一个个来看吧,首先还是先把文件操作封装下,目前只要读取文件的操作。

actual fun readContent(filePath: String) = memScoped {
    val st = alloc<stat>()
    stat(filePath, st.ptr)
    val size = st.st_size
    val buf = allocArray<ByteVar>(size)
    val f = fopen(filePath, "rb")
    fread(buf, 1UL, size.toULong(), f)
    fclose(f)
    buf.readBytes(size.toInt())
}

用这个函数可以得到一个 Kotlin 的 ByteArray 类型的对象,所以我们可以这样来获得 ByteReadPacketIoBuffer 实例:

val input1 = ByteReadPacket(readContent("a.txt"))
val input2 = readContent("a.txt").let { IoBuffer(it.toCValues().ptr, it.size) }

这两种做法有什么不同? 最本质的不同在于,ByteReadPacketcommon 里实现,因此可以把相关的代码写在 common 内,而 IoBuffer 是区分平台实现,所以需要在各个平台的 target 里都写一遍,所以为了方便起见,我最终选择了使用 ByteReadPacket

最终实现的代码如下:

expect class File {
    ... ...
    fun readContent(): ByteArray
    ... ...
}
fun File.asInput() = ByteReadPacket(readContent())

这样,就可以在 Ktor-Client 内直接使用 asInput() 了,和 JVM 下一样方便:

private fun buildPostFileBody(params: Map<String, String>, files: Map<String, String>) = MultiPartFormDataContent(
    formData {
        params.forEach { (t, u) -> append(t, u) }
        files.forEach { (t, u) ->
            appendInput(t, headersOf("Content-Type", "application/octet-stream")) {
                File(u).asInput()
            }
        }
    }
)

最后再说一句,Ktor 的文档真的让人无语(戳此处去官方看看),还请多少给点示例吧,不然真的是面向运气Coding,全靠猜。

上一篇下一篇

猜你喜欢

热点阅读