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

作者: 何晓杰Dev | 来源:发表于2019-06-18 14:13 被阅读15次

    首先给大家一段官方文档里的代码,来看看用 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,全靠猜。

    相关文章

      网友评论

        本文标题:[Kotlin/Native] 踩坑,Ktor-Client 如

        本文链接:https://www.haomeiwen.com/subject/okcfqctx.html