Загрузка дооснащения сохраняет данные формы внутри загружаемого файла, повреждая его.

Я использую Retrofit 2 для загрузки аудиофайла в службу хранилища BLOB-объектов Azure через REST API Azure.

Загрузка работает, но файл, хранящийся в контейнере больших двоичных объектов Azure, поврежден, так как помимо аудиоданных он содержит то, что выглядит как заголовки HTTP. Например, это содержимое одного загруженного файла:

--3c88cdb1-5946-432d-a129-cc8e930d014c
Content-Disposition: form-data; name="tape"; 
filename="/data/user/0/blahblah.mp4"
Content-Type: audio/mp4
Content-Length: 8365

...expected binary data blah blah blah ....
--3c88cdb1-5946-432d-a129-cc8e930d014c--

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

Моя функция загрузки выглядит так:

    val tapeFile = File(fileName)
    val tapePart = tapeFile.asRequestBody("audio/mp4".toMediaType())
    val tapeBodyPart = MultipartBody.Part.createFormData("tape",tapeFile.absolutePath, tapePart)
    tapeAzureWebService.uploadTape(url, tapeBodyPart).enqueue(object : Callback<ResponseBody> {
        override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
            if (response.isSuccessful) { 
    etc etc

Мой интерфейс интерфейса Retrofit выглядит следующим образом:

@Multipart
@PUT
fun uploadTape(@Url url: String,
               @Part tape: MultipartBody.Part): Call<ResponseBody>

(Он использует @URL, потому что я использую Azure SAS с динамическими URL-адресами с проверкой подлинности, встроенной в URL-адрес в виде серии строк запроса, и это работает очень хорошо и, кстати, является подсказкой для всех, кто наткнется на это, поскольку он не позволяет Retrofit кодировать URL-адрес и запрос.)

И мой клиент OKHttp выглядит так, добавляя некоторые заголовки, которые требует Azure:

class TapeAzureWebServiceAPI {

  fun service() : TapeAzureWebService {

    val headerInterceptor = object: Interceptor {
        override fun intercept(chain: Interceptor.Chain): Response {
            val original = chain.request()
            val requestBuilder = original.newBuilder()
                    .header("x-ms-version", "2015-12-11")
                    .header("x-ms-blob-type","BlockBlob")
            val request = requestBuilder.build()
            return chain.proceed(request)
        }
    }

    val loggingInterceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
        override fun log(message: String) {
            logI("retrofit: $message")
        }
    }).setLevel(HttpLoggingInterceptor.Level.BODY)

    val client : OkHttpClient = OkHttpClient.Builder().apply {
        this.addInterceptor(headerInterceptor)
        this.addInterceptor(loggingInterceptor)
    }.build()

    val retrofit = Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl(AZURE_URL)
            .client(client)
            .build()
    return retrofit.create(TapeAzureWebService::class.java)
  }
}

Если я использую простой RequestBody, а не составную форму, я все равно получаю то же повреждение аудиофайла, хотя в аудиофайле меньше заголовков.

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

Благодарность

Джон


person John    schedule 04.04.2020    source источник
comment
whether Azure simply doesn't like multipart form data - Вот он. Что бы вы ни загружали, хранилище Azure сохранит как есть.   -  person Gaurav Mantri    schedule 04.04.2020
comment
Есть ли способ загрузки через Retrofit или я должен использовать какую-то другую библиотеку/технику? Кстати, спасибо за быстрый ответ!   -  person John    schedule 04.04.2020
comment
К сожалению, я не знаком с Retrofit, поэтому я могу ответить на этот вопрос. Однако вам нужно загрузить необработанный поток байтов в хранилище Azure, не преобразовывая его в данные составной формы.   -  person Gaurav Mantri    schedule 04.04.2020
comment
Спасибо. Это указывает мне правильное направление.   -  person John    schedule 04.04.2020
comment
Может просто попробовать с RequestBody? По сути, просто используйте tapePart из val tapePart = tapeFile.asRequestBody("audio/mp4".toMediaType())?   -  person Gaurav Mantri    schedule 04.04.2020
comment
Это по-прежнему создает ту же проблему, только с немного другими заголовками.   -  person John    schedule 05.04.2020


Ответы (1)


удалить @Multipart просто добавить,

@Headers(  "x-ms-blob-type: BlockBlob", "x-ms-blob-content-type: image/png")
@PUT
suspend fun uploadDocument(@Url url: String, @Body request: RequestBody)

и передать тело запроса как,

val mediaType = "image/png".toMediaTypeOrNull()
val body = yourImageFile.asRequestBody(mediaType)
person madhu gautam    schedule 18.03.2021