Я пытаюсь получить доступ к Stability AI в Android с помощью клиента Ktor, код выглядит так
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"))
}
}
что приводит к
400 Неверный ответ на запрос {"успех":false,"message":"неверный формат Запрос данных формы. Заголовок Content-Disposition в части FormData пропало имя."}
API стабильности требует, чтобы мы предоставили ему данные из нескольких частей формы, но куда мне следует включить этот заголовок размещения содержимого? Я проверил официальную документацию, где приведен пример загрузки файла изображения, который выглядит следующим образом.
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")
}
}
в этом примере файл загружается, и его имя файла отправляется с использованием заголовка Content-Disposition, но в моем случае у меня нет файла для загрузки, но я пытался передать строку в качестве имени, но это дало ту же ошибку, API работает в почтальоне с теми же параметрами
РЕДАКТИРОВАТЬ 1: я пытался включить тип контента в блок formData, но это было бесполезно.
formData {
append("prompt", text, Headers.build {
append(HttpHeaders.ContentType, "text/plain")
}) }
Редактировать 2: После добавления дополнительных кавычек, как было предложено в ответе и комментариях, я смог двигаться дальше, большое спасибо, окончательный код выглядит так
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"))
}
}





У меня была такая же проблема в C#, и я также получил сообщение об ошибке Malformed FormData request. Content-Disposition header in FormData part is missing a name. По какой-то странной причине мне пришлось заключить имена параметров в две кавычки.
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);
большое спасибо за ответ, мне помогло добавление кавычек в подсказку
Попробуйте использовать Fiddler или Wireshak, чтобы зафиксировать неправильный и правильный запрос. Сравните их. В моем случае точно такая же ошибка была вызвана отсутствием кавычек в значении имени
Content-Disposition:Content-Disposition: form-data; name=promptvsContent-Disposition: form-data; name = "prompt"