使用OkHttp上传到预先签名的S3 URL失败

问题描述 投票:1回答:1

当我尝试使用OkHttp 3.9.1将文件上传到Amazon S3的预签名URL时,我收到了SSL异常:SSLException: Write error: ssl=0xa0b73280: I/O error during system call, Connection reset by peer

这与another SO question中的问题相同,但在我的情况下,它总是失败。我只上传大小超过1MiB的文件,我没有尝试过小文件。

正如我在该问题的答案中提到的,切换到Java的HttpURLConnection解决了问题并且上传工作完美。

这是我的RequestBody实现(在Kotlin中)从Android的Uri上传文件,我使用OkHttp的.put()方法:

class UriRequestBody(private val file: Uri, private val contentResolver: ContentResolver, private val mediaType: MediaType = MediaType.parse("application/octet-stream")!!): RequestBody() {
    override fun contentLength(): Long = -1L

    override fun contentType(): MediaType? = mediaType

    override fun writeTo(sink: BufferedSink) {
        Okio.source((contentResolver.openInputStream(file))).use {
            sink.writeAll(it)
        }
    }
}

这是我的HttpURLConnection实现:

private fun uploadFileRaw(file: Uri, uploadUrl: String, contentResolver: ContentResolver) : Int {
    val url = URL(uploadUrl)
    val connection = url.openConnection() as HttpURLConnection
    connection.doOutput = true
    connection.requestMethod = "PUT"
    val out = connection.outputStream

    contentResolver.openInputStream(file).use {
        it.copyTo(out)
    }

    out.close()
    return connection.responseCode
}

什么是OkHttp做的不同,所以它可以导致这个SSL异常?

编辑:

这是上传文件的OkHttp代码(使用默认的application/octet-stream mime类型):

val s3UploadClient = OkHttpClient().newBuilder()
    .connectTimeout(30_000L, TimeUnit.MILLISECONDS)
    .readTimeout(30_000L, TimeUnit.MILLISECONDS)
    .writeTimeout(60_000L, TimeUnit.MILLISECONDS)
    .retryOnConnectionFailure(true)
    .build()

val body: RequestBody = UriRequestBody(file, contentResolver)
val request = Request.Builder()
        .url(uploadUrl)
        .put(body)
        .build()

s3UploadClient.newCall(request).execute()

这是生成预签名上传网址的JavaScript服务器代码:

const s3 = new aws.S3({
    region: 'us-west-2',
     signatureVersion: 'v4'
});
const signedUrlExpireSeconds = 60 * 5;

const signedUrl = s3.getSignedUrl('putObject', {
    Bucket: config.bucket.name,
    Key: `${fileName}`,
    Expires: signedUrlExpireSeconds
});
android amazon-web-services amazon-s3 okhttp okhttp3
1个回答
1
投票

这似乎适用于改造库:

fun uploadImage(imagePath: String, directUrl: String): Boolean {
    Log.d(TAG, "Image: ${imagePath}, Url: $directUrl")
    val request = Request.Builder()
            .url(directUrl)
            .put(RequestBody.create(null, File(imagePath)))
            .build()
    val response = WebClient.getOkHttpClient().newCall(request).execute()
    return response.isSuccessful
}
© www.soinside.com 2019 - 2024. All rights reserved.