2

I'm creating a Kotlin/Jvm (without Android Sdk) application that interacts with a instance of a Parse Server (Back4App). Unfortunately, parse doesn't provide a Sdk implementation to use with Java/Kotlin without Android.

So I'm using the rest Api. Now I trying to upload a image from my disk into Back4App file server. In the doc there is snippet using curl. But I wasn't able to translate into a Retrofit service:

curl -X POST \
  -H "X-Parse-Application-Id: 4MGgDJ0ZiQloXoSTE2I9VM6YUYIz8EwCKF4pK7zr" \
  -H "X-Parse-REST-API-Key: ${REST_API_KEY}" \
  -H "Content-Type: image/jpeg" \
  --data-binary '@myPicture.jpg' \
  https://YOUR.PARSE-SERVER.HERE/parse/files/pic.jpg

So I based my implementation in this article and other snippets from GitHub and created a retrofit service for it:

@Multipart
@POST("/parse/files")
fun upload(
    @Part file: MultipartBody.Part
): Call<ResponseBody>

And call:

var file = File("assets/escudo.png")

var requestFile = RequestBody.create(MediaType.parse("**/image"), file)

var body = MultipartBody.Part.createFormData("picture", file.name, requestFile)
var r = getService().upload(body).execute()

I created the retrofit instance as below:

fun getService(): ParserService {

    val retrofit = Retrofit
        .Builder()
        .baseUrl("https://parseapi.back4app.com")
        .addConverterFactory(GsonConverterFactory.create())
        .client(createClient()).build()

    return retrofit.create(ParserService::class.java)
}

fun createClient(): OkHttpClient {
    return OkHttpClient.Builder().addInterceptor(createHeadInterceptor()).build()
}

fun createHeadInterceptor(): Interceptor {
    return HeaderInterceptor()
}

class HeaderInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response =
        chain.run {
            val credentials = CredentialsUtils.readCredentials()
            log.info { credentials }
            proceed(
                request().newBuilder()
//                    .addHeader("Content-Type", "application/json")
                     .addHeader("Content-Type", "image/png")
                    .addHeader("X-Parse-Application-Id", credentials.back4appAppId)
                    .addHeader("X-Parse-REST-API-Key", credentials.back4appRestApiKey)
                    .build()
            )
        }
}

I was able to use it to posting Json data (by uncommenting the content/type header). But when I tried to upload an image I receive this response:

Response{protocol=h2, code=400, message=, url=https://parseapi.back4app.com/parse/files}

enter image description here

More info:

enter image description here

-- EDIT

I tried a different approuch without Retrofit, it gives a 201 response code and gives me an objectId, but it doesn't upload the file:

val file2 = File("assets/escudo.png")
    val serverUrl = "https://parseapi.back4app.com/classes/myfiles"
    val url = URL(serverUrl)
    val conn = url.openConnection() as HttpURLConnection
    conn.requestMethod = "POST"
    conn.doOutput = true

    val postData = file2.readBytes()

    conn.addRequestProperty("Content-length", postData.size.toString())
    conn.setRequestProperty("Content-Type", "image/*")
    conn.setRequestProperty("X-Parse-Application-Id", credentials.back4appAppId)
    conn.setRequestProperty("X-Parse-REST-API-Key", credentials.back4appRestApiKey)

    val outputStream = DataOutputStream(conn.outputStream)
    outputStream.write(postData)
    outputStream.flush()

    println(conn.responseCode)

there should be a new column with the file, right?

-- EDIT

Trying now using Khttp:

    val file = File("assets/foto.jpg")
    val file2 = File("assets/escudo.png")
    val serverUrl = "https://parseapi.back4app.com/classes/myfiles"


    val files = listOf(FileLike("foto.jpg", file), FileLike("escudo.png", file2))
    val response = post(serverUrl, headers = getHeaders(), files = files)

    println(response)
    println(response.text)

}

fun getHeaders(): Map<String, String> {
    return mapOf(
        "Content-Type" to "image/*",
        "X-Parse-Application-Id" to credentials.back4appAppId,
        "X-Parse-REST-API-Key" to credentials.back4appRestApiKey
    )
}

Getting this error:

<Response [400]>
{"error":"Unexpected token - in JSON at position 0"}
alexpfx
  • 6,412
  • 12
  • 52
  • 88
  • 1
    wild guess ... try doing a byteArray body in a POST ( dont use mime multipart ) as in the answer here : https://stackoverflow.com/questions/49188722/how-to-http-request-by-post-method-with-kotlin – Robert Rowntree Mar 10 '19 at 03:35
  • I tried to do it as the snipped in that answer, it gives a http 200 code but it doesn't upload the file. I think I'm missing something... – alexpfx Mar 10 '19 at 17:18
  • whats in the body of the response? does it match the parse.rest.docs for POST on an object? response should include OID and date/time stamp... check the docs – Robert Rowntree Mar 10 '19 at 20:52
  • I've edit the question providing more information. Yes, when it answer with http 200 it answer with this json: [text={"objectId":"YLWzWbaMjq","createdAt":"2019-03-10T21:00:25.171Z"}]. But it doesn't match the docs. The response should include a link to the uploaded file. – alexpfx Mar 10 '19 at 21:01
  • 1
    https://gist.github.com/cmcdonaldca/5668086?#file-parsetest-java-L32 you are using incorrect url for parse rest upload . – Robert Rowntree Mar 10 '19 at 21:28
  • I suspected it. But back4app doesn't say anything about which url to use to upload files, only Parse Platform does (https://docs.parseplatform.org/rest/guide/#files). I'm searching in the docs of back4app, I think it might not be possible to upload this way. – alexpfx Mar 10 '19 at 21:35
  • As I see I could make a column in my classes and give a type "File". Maybe it's the only way for back4app. So I have to wrap it into a Json, isn't it? – alexpfx Mar 10 '19 at 21:41
  • https://docs.parseplatform.org/rest/guide/#associating-with-objects : revoew the docs on what to do after u have uploaded the file so, you then have a ref to what was uploaded in an instance of a parse class.. ITS 2 STEPS. – Robert Rowntree Mar 11 '19 at 15:49
  • I got that but the problem i think is with the first step. ParsePlatform is a open source platform you could host anywhere. Back4App hosts a Parse instance. These docs are about parse platform. I found nothing in Back4App docs about upload files using their rest service. So I think it's not possible or it's not that same way. I tried to upload a file using Curl based on that same example (from ParsePlatform docs) and I received the same error: – alexpfx Mar 12 '19 at 00:31
  • {"error":"Unexpected token � in JSON at position 0"}. That object id i received before is not a response from a 'file upload'. A response valid response should be anything like this: Status: 201 Created Location: http://files.parsetfss.com/bc9f32df-2957-4bb1-93c9-ec47d9870a05/tfss-db295fb2-8a8b-49f3-aad3-dd911142f64f-hello.txt – alexpfx Mar 12 '19 at 00:34

1 Answers1

1

If you're using Back4App, the correct Server URL is:

https://parseapi.back4app.com/files/pic.jpg

nataliec
  • 502
  • 4
  • 14
  • 1
    Thank you, it works with this url. I received a correct response, I could access the image via link, but how I could browse by these images in the site? I look for it in dashboard but cannot saw it. – alexpfx Mar 12 '19 at 22:49