Kotlin Multipart Requests на Android - PullRequest
       20

Kotlin Multipart Requests на Android

0 голосов
/ 06 ноября 2018

Я был бы очень признателен за помощь в решении этой проблемы.

Я пытаюсь опубликовать изображение в API синтаксического анализа квитанции и у меня возникают проблемы при создании фактического запроса.

Я прочитал и использовал большую часть кода этой статьи , написанной tarek на Medium, для создания класса MultiPart (с https), например:

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 я не могу добавить к нему какие-либо заголовки или данные. Например. если два addHeaderField -коллока в вызове функции parseReceipt перемещены в init -блок в Multipart.kt , заголовки находятся в запросе, но в противном случае - нет.

Что я здесь не так делаю?

1 Ответ

0 голосов
/ 08 ноября 2018

Использование сторонней библиотеки решило мою проблему:

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)
                        }
                )
            }
...