I would really appreciate some help with the problem I'm facing.
I'm trying to post an image to a receipt parsing API and have problems constructing the actual request.
I have read and used much of the code from this article written by tarek on Medium to create a MultiPart class (with https) like the following:
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()
}
}
}
And I'm calling the above class from:
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()
}
The problem is that after initialisation of a Multipart object, I cannot append any headers or data to it. E.g. if the two addHeaderField-calls within the parseReceipt-function call is moved to the init-block in Multipart.kt, the headers are in the request, but otherwise not.
What am I doing wrong here?
Usage of a third-party library solved my problem:
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)
}
)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With