问题描述
我在使用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 (将#修改为@)