安卓上的Kotlin多部分请求

3

我希望你能帮我解决问题。

我正在尝试将一张图片发布到收据解析API,但在构建实际请求时遇到了问题。

我已经阅读并使用了这篇文章中由tarek在Medium上编写的代码来创建一个类似以下的带有https的MultiPart类:

Multipart.kt

package com.example.skopal.foodme.services

import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.io.PrintWriter
import java.net.URL
import javax.net.ssl.HttpsURLConnection

class Multipart
/**
 * This constructor initializes a new HTTPS POST request with content type
 * is set to multipart/form-data
 * @param url
 * *
 * @throws IOException
 */
@Throws(IOException::class)
constructor(url: URL) {

    companion object {
        private val LINE_FEED = "\r\n"
        private val maxBufferSize = 1024 * 1024
        private val charset = "UTF-8"
    }

    // creates a unique boundary based on time stamp
    private val boundary: String = "===" + System.currentTimeMillis() + "==="
    private val httpsConnection: HttpsURLConnection = url.openConnection() as HttpsURLConnection
    private val outputStream: OutputStream
    private val writer: PrintWriter

    init {

        httpsConnection.setRequestProperty("Accept-Charset", "UTF-8")
        httpsConnection.setRequestProperty("Connection", "Keep-Alive")
        httpsConnection.setRequestProperty("Cache-Control", "no-cache")
        httpsConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary)
        httpsConnection.setChunkedStreamingMode(maxBufferSize)
        httpsConnection.doInput = true
        httpsConnection.doOutput = true    // indicates POST method
        httpsConnection.useCaches = false
        outputStream = httpsConnection.outputStream
        writer = PrintWriter(OutputStreamWriter(outputStream, charset), true)
    }

    /**
     * Adds a upload file section to the request
     * @param fieldName  - name attribute in <input type="file" name="..."></input>
     * *
     * @param uploadFile - a File to be uploaded
     * *
     * @throws IOException
     */
    @Throws(IOException::class)
    fun addFilePart(fieldName: String, uploadFile: File, fileName: String, fileType: String) {
        writer.append("--").append(boundary).append(LINE_FEED)
        writer.append("Content-Disposition: file; name=\"").append(fieldName)
            .append("\"; filename=\"").append(fileName).append("\"").append(LINE_FEED)
        writer.append("Content-Type: ").append(fileType).append(LINE_FEED)
        writer.append(LINE_FEED)
        writer.flush()

        val inputStream = FileInputStream(uploadFile)
        inputStream.copyTo(outputStream, maxBufferSize)

        outputStream.flush()
        inputStream.close()
        writer.append(LINE_FEED)
        writer.flush()
    }

    /**
     * Adds a header field to the request.
     * @param name  - name of the header field
     * *
     * @param value - value of the header field
     */
    fun addHeaderField(name: String, value: String) {
        writer.append("$name: $value").append(LINE_FEED)
        writer.flush()
    }

    /**
     * Upload the file and receive a response from the server.
     * @param onSuccess
     * *
     * @param onFailure
     * *
     * @throws IOException
     */
    @Throws(IOException::class)
    fun upload(onSuccess: (String) -> Unit, onFailure: ((Int) -> Unit)? = null) {
        writer.append(LINE_FEED).flush()
        writer.append("--").append(boundary).append("--")
                .append(LINE_FEED)
        writer.close()

        try {
            // checks server's status code first
            val status = httpsConnection.responseCode
            if (status == HttpsURLConnection.HTTP_OK) {
                val reader = BufferedReader(InputStreamReader(httpsConnection.inputStream))
                val response = reader.use(BufferedReader::readText)
                httpsConnection.disconnect()
                onSuccess(response)
            } else {
                onFailure?.invoke(status)
            }

        } catch (e: IOException) {
            e.printStackTrace()
        }

    }

}

我正在从以下位置调用上述类:

ReceiptRecognitionApi.kt

fun parseReceipt(file: File, cb: (String) -> Unit) {
    println("parseReceipt_1")

    Thread {
        val multipartReq = Multipart(URL(baseUrl))
        multipartReq.addHeaderField("apikey", taggunApiKey)
        multipartReq.addHeaderField("Accept", "application/json")

        multipartReq.addFilePart("file", file, "receipt.jpg", "image/jpeg")

        multipartReq.upload(
                onSuccess = { response: String ->
                    cb(response)
                },
                onFailure = { responseCode: Int ->
                    cb("$responseCode")
                })

    }.start()
}

问题在于初始化 Multipart 对象后,不能将任何标头或数据附加到它上面。例如,如果 parseReceipt 函数内的两个 addHeaderField 调用被移动到 Multipart.kt 中的 init 块中,则标头会在请求中出现,否则不会。

我在这里做错了什么?


你为什么不使用 HTTP 库(例如 Fuel)呢? - m0skit0
1
@m0skit0 除了让应用程序更安全,我使用的 API(taggun)还要求使用 HTTPS 连接。 - Peter Skopal
1
让应用程序安全?你是什么意思?使用库并不会使您的应用程序变得不安全(除非您是安全专家,否则很可能相反)。大多数HTTP库都支持开箱即用的HTTPS。 - m0skit0
@m0skit0 哦,抱歉,我不知道Fuel支持HTTPS,这也是我所说的“安全”。我会研究一下并尝试用这种方式解决它。 - Peter Skopal
1
你可以在这里看到大多数示例实际上都使用HTTPS :) 在我看来,最好始终使用这些库,因为这些人很可能已经解决了你面临的相同问题,除非你想自己深入研究HTTP,这就是我问你是否有任何不使用更高级库的具体原因的原因 :) - m0skit0
@m0skit0 好的 :) 感谢您的建议,我稍后会在这里发布解决方案。 - Peter Skopal
1个回答

2

使用第三方库解决了我的问题:

Fuel.upload(path = baseUrl, method = Method.POST)
            .header(TaggunConstants.taggunHeader(taggunApiKey))
            .dataParts { _, _ -> listOf(DataPart(file, "file", "image/jpeg")) }
            .responseJson { _, _, result ->
                result.fold(
                        success = { data ->
                            cb(gson.fromJson(data.content, Receipt::class.java))
                        },
                        failure = { error ->
                            println("An error of type ${error.exception} happened: ${error.message}")
                            cb(null)
                        }
                )
            }

谢谢!这正是我所寻找的。 - Ryan Godlonton-Shaw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接