android - 改造上传将表单数据存储在上传文件中,从而损坏它

标签 android azure retrofit2 azure-blob-storage azure-sas

我正在使用 Retrofit 2 通过 Azure 的 REST API 将音频文件上传到 Azure blob 存储服务。

上传似乎有效,但存储在 Azure blob 容器中的文件已损坏,因为它包含似乎是 HTTP header 的音频数据。例如,以下是一个上传文件的内容:

--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 要求的一些 header :

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 而不是多部分表单,尽管音频文件中的 header 较少,但音频文件仍然会出现相同的损坏情况。

我已经研究了很长时间,但我无法判断是否是我在 Retrofit 中做错了什么,Azure 是否需要不同的 header ,或者 Azure 是否只是不喜欢多部分表单数据。

谢谢

约翰

最佳答案

删除@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)

关于android - 改造上传将表单数据存储在上传文件中,从而损坏它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61027998/

相关文章:

java - AnglePicker 类似 Android API19 TimePicker

android - 如何关闭通过膨胀布局获得的按钮的 onClick 方法中的警告框?

android - CardView 内的 ExpandableListView 不会改变其父级高度

java - 从 ListView 中调用 fragment

ECAcc 服务器上的 Azure CDN 504 网关超时

azure - 如何在同一个 Azure Web App Service 中托管多个应用程序?

c# - xamarin.forms 和 Microsoft Azure 入门

android - 挂起 Coroutines 的执行等待回调

android - 我如何从 SharedPreference 得到这个作为我的响应对象?

android - 为什么大多数人在retrofit中使用拦截器添加认证头?