I am trying to access Stability AI in android using Ktor Client the code looks like this
suspend fun generateImageOfText(text: String,aspectRatio: String = "1:1", apiKey: String): Result {
return try {
val response = httpClient.post("https://api.stability.ai/v2beta/stable-image/generate/ultra") {
header(HttpHeaders.Authorization, "Bearer $apiKey")
header(HttpHeaders.Accept, "application/json")
setBody(MultiPartFormDataContent(
formData {
append("prompt", text)
},
boundary = "WebAppBoundary"
))
}
val resultText = response.bodyAsText()
Log.d("APIConnector", "generateImageOfText: status ${response.status} result $resultText")
Result.Success(resultText)
}catch (e: Exception){
e.printStackTrace()
(Result.Error(e.message ?: "Unknown error"))
}
}
which results in
400 Bad Request response {"success":false,"message":"Malformed FormData request. Content-Disposition header in FormData part is missing a name."}
Stability API requires us to provide it with Multi part form data but where should I include this content-disposition header? I did check in official documentation where it gives us an example to upload an image file which looks like this
val client = HttpClient(CIO)
val response: HttpResponse = client.post("http://localhost:8080/upload") {
setBody(MultiPartFormDataContent(
formData {
append("description", "Ktor logo")
append("image", File("ktor_logo.png").readBytes(), Headers.build {
append(HttpHeaders.ContentType, "image/png")
append(HttpHeaders.ContentDisposition, "filename=\"ktor_logo.png\"")
})
},
boundary = "WebAppBoundary"
)
)
onUpload { bytesSentTotal, contentLength ->
println("Sent $bytesSentTotal bytes from $contentLength")
}
}
in this example a file is getting uploaded and it's file name is sent using Content-Disposition header but in my case I do not have any file to upload still I did try to pass in a string as name but It gave the same error, API is working in postman with the same parameters
EDIT 1: I have tried to include Content Type in my formData block but it was of no use
formData {
append("prompt", text, Headers.build {
append(HttpHeaders.ContentType, "text/plain")
}) }
Edit 2: After adding additional quotes as suggested in the answer and comments I was able to move forward thanks a lot, final code looks like this
suspend fun generateImageOfText(text: String,aspectRatio: String = "1:1", apiKey: String): Result {
return try {
val response = httpClient.post("https://api.stability.ai/v2beta/stable-image/generate/core") {
header(HttpHeaders.Authorization, "Bearer $apiKey")
header(HttpHeaders.Accept, "application/json")
setBody(MultiPartFormDataContent(
formData {
append("\"prompt\"", text, Headers.build {
append(HttpHeaders.ContentDisposition, "form-data; name=\"prompt\"")
})
}
))
}
val result: Base64Result = response.body()
Result.Success(result)
}catch (e: Exception){
e.printStackTrace()
(Result.Error(e.message ?: "Unknown error"))
}
}
I had the same problem in C# and also received the error message Malformed FormData request. Content-Disposition header in FormData part is missing a name
. For some strange reason I had to wrap the parameter names in two quotes.
var prompt = "Cat on the moon";
var format = "3:2";
using var content = new MultipartFormDataContent()
{
{ new StringContent(prompt), "\"prompt\"" },
{ new StringContent("png"), "\"output_format\"" },
{ new StringContent(format), "\"aspect_ratio\"" }
};
var url = "https://api.stability.ai/v2beta/stable-image/generate/ultra";
var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = content
};
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("image/*"));
var response = await _httpClient.SendAsync(request);