Android如何使用okhttp

问题描述

我在使用Kotlin和Okhttp的Android上使用FileInputStream遇到问题。我想使用API​​发布视频文件,但是如果该文件大于128mb,则必须逐块上传。

因此,在我的请求标头中,我必须指定Content-Range (类似这样的内容:Content-Range bytes 0-10485759/189305151

我只需要发布文件的这一部分,这就是为什么我要使用FileInputStream,然后对每个块重复一次。

我还使用FileInputStream来避免在本地拆分文件或将其放入内存中。

为此,我正在使用FileInputStream的read()方法来获取块

fileStream.read(b,chunkLength.toInt())

但是当我上传FileInputStream时,它没有上传正确的文件长度。

在我的文件大小为189.305.151的情况下,最终上传的文件大小为1.614.427.758 (1.614.427.758是所有filestream.available()一起添加的内容)

我不知道我做对的方式是否正确,但这是我找到的唯一解决方案,希望有人可以帮助我。

这是我的完整代码:

private val chunkLength = (1024L * 1024L) * 10L
private fun uploadBigFile(videoId: String,file: File,callBack: CallBack<Video>){
    val fileLength = file.length()
    try {
        var b = ByteArray(chunkLength.toInt())
        for (offset in 0 until fileLength step chunkLength){
            val fileStream = file.inputStream()

            var currentPosition = (offset.toInt()) + chunkLength.toInt() - 1

            // foreach chunk except the first one
            if(offset > 0){
                // skip all the chunks already uploaded
                fileStream.skip(offset)
            }
            // if this is the last chunk
            if(currentPosition > fileLength){
                fileStream.read(ByteArray((currentPosition - fileLength).toInt()))
                currentPosition = file.length().toInt() - 1

            }else{
                fileStream.read(b,chunkLength.toInt())
                Log.e("stream after read",fileStream.available().toString())
                Log.e("------","------")

            }

            // FileInputStream as RequestBody
            val videoFile = RequestBodyUtil.create(fileStream)

            val body = MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file","file",videoFile)
                .build()

            Log.e("bytes","${offset.toInt()}-$currentPosition/${file.length().toInt()}")

            val request = Request.Builder()
                .url("$baseUri/videos/$videoId/source")
                .addHeader(
                    "Content-Range","bytes ${offset.toInt()}-$currentPosition/${file.length().toInt()}"
                )
                .post(body)
                .build()

            executor.execute(request,videoChunkTransformer,callBack)
        }
    }catch (e: Exception){
        Log.e("error",e.toString())
    }
}

有我的RequestBodyUtil类:

class RequestBodyUtil {
companion object{
    fun create(inputStream: InputStream): RequestBody {
        return object : RequestBody() {
            override fun contentType(): MediaType? {
                return null
            }

            override fun contentLength(): Long {
                return try {
                    inputStream.available().toLong()
                } catch (e: IOException){
                    0
                }
            }

            @Throws(IOException::class)
            override fun writeTo(sink: BufferedSink) {
                try {
                    var source = inputStream.source()
                    sink.writeAll(source.buffer())
                }catch (e: IOException){
                    e.printStackTrace()
                } finally {
                    inputStream.closeQuietly()
                }
            }
        }
    }
}}

编辑:我在代码上添加了一些更改,但是仍然存在一些问题。

我无法上载输入流的块,因此我决定将每个块复制到字节数组输出流中,但是使用OkHttp和Okio我无法上载OutputStream(我知道将块存储在RAM上,但这是找到的唯一解决方案)。这就是为什么我将其更改为字节数组输入流然后可以上传的原因。 对于某些文件,我可以上传并且可以正常工作,但是当文件太大时,会出现诸如此类的OutOfMemory错误:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: video.api.androidkotlinsdkexample,PID: 8627
java.lang.OutOfMemoryError: Failed to allocate a 52428816 byte allocation with 6291456 free bytes and 9999KB until OOM,target footprint 532923344,growth limit 536870912
    at java.util.Arrays.copyOf(Arrays.java:3161)
    at java.io.ByteArrayOutputStream.toByteArray(ByteArrayOutputStream.java:191)
    at video.api.androidkotlinsdk.api.VideoApi.uploadBigFile(VideoApi.kt:159)
    at video.api.androidkotlinsdk.api.VideoApi.access$uploadBigFile(VideoApi.kt:25)
    at video.api.androidkotlinsdk.api.VideoApi$upload$1.onSuccess(VideoApi.kt:375)
    at video.api.androidkotlinsdk.api.VideoApi$upload$1.onSuccess(VideoApi.kt:357)
    at video.api.androidkotlinsdk.http.HttpRequestExecutor$execute$1$onResponse$1.run(HttpRequestExecutor.kt:35)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7356)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

有代码:

private fun uploadBigFile(videoId: String,callBack: CallBack<Video>){
    val fileLength = file.length()
    try {
        var b = ByteArray(chunkLength.toInt())
        var bytesReads = 0

        for (offset in 0 until fileLength step chunkLength){
            var readBytes: Int
            val fileStream = file.inputStream()
            var currentPosition = (offset.toInt()) + chunkLength.toInt() - 1

            // foreach chunk except the first one
            if(offset > 0){
                // skip all the chunks already uploaded
                fileStream.skip(offset)
            }

            // if this is the last chunk
            if(currentPosition > fileLength){
                readBytes = fileStream.read(b,(fileLength - bytesReads).toInt())
                currentPosition = file.length().toInt() - 1


            }else{
                readBytes = fileStream.read(b,chunkLength.toInt())

            }
            bytesReads += readBytes

            val byteArrayOutput = ByteArrayOutputStream()
            byteArrayOutput.write(b,readBytes)

            val byteArrayInput = ByteArrayInputStream(byteArrayOutput.toByteArray())


            val videoFile = RequestBodyUtil.create(byteArrayInput)

            val body = MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file",callBack)
            byteArrayOutput.close()
            byteArrayInput.close()
            fileStream.close()

        }
    }catch (e: Exception){
        Log.e("error",e.toString())
    }
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...