首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >安卓-如何使用OkHTTP以块方式上传视频?

安卓-如何使用OkHTTP以块方式上传视频?
EN

Stack Overflow用户
提问于 2017-07-15 23:39:55
回答 2查看 5.7K关注 0票数 6

请看我下面的代码,我用来上传一个视频到服务器。然而,对于足够大的视频,我得到了一个OutOfMemory异常。

代码语言:javascript
运行
复制
        InputStream stream = getContentResolver().openInputStream(videoUri);
        byte[] byteArray = IOUtils.toByteArray(stream);
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", "fname",
                        RequestBody.create(MediaType.parse("video/mp4"), byteArray))
                .build();
        Request request = new Request.Builder()
                .url(uploadURL)
                .post(requestBody)
                .build();

        OkHttpClient client = new OkHttpClient.Builder().build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
            }
        });

有人能为我指出如何避免OutOfMemory异常的正确方向吗?有办法从InputStream到requestBody吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-07-16 00:39:05

您可以创建一个自定义RequestBody来流数据。您必须注意:由于OkHttp可能决定重试请求,所以它可能会被多次重用。确保每次都可以重新打开InputStream

代码语言:javascript
运行
复制
ContentResolver contentResolver = context.getContentResolver();
final String contentType = contentResolver.getType(videoUri);
final AssetFileDescriptor fd = contentResolver.openAssetFileDescriptor(videoUri, "r");
if (fd == null) {
    throw new FileNotFoundException("could not open file descriptor");
}
RequestBody videoFile = new RequestBody() {
    @Override public long contentLength() { return fd.getDeclaredLength(); }
    @Override public MediaType contentType() { return MediaType.parse(contentType); }
    @Override public void writeTo(BufferedSink sink) throws IOException {
        try (InputStream is = fd.createInputStream()) {
            sink.writeAll(Okio.buffer(Okio.source(is)));
        }
    }
};
RequestBody requestBody = new MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        .addFormDataPart("file", "fname", videoFile)
        .build();
Request request = new Request.Builder()
        .url(uploadURL)
        .post(requestBody)
        .build();
client.newCall(request).enqueue(new Callback() {
    @Override public void onFailure(Call call, IOException e) {
        try {
            fd.close();
        } catch (IOException ex) {
            e.addSuppressed(ex);
        }
        Log.e(TAG, "failed", e);
    }
    @Override public void onResponse(Call call, Response response) throws IOException {
        fd.close();
    }
});
票数 7
EN

Stack Overflow用户

发布于 2021-04-25 09:57:32

  1. 尝试使用Uri / ContentResolver/ ParcelFileDescriptor之一将AssetFileDescriptor转换为InputStream
  2. okio.Source InputStream (关于源/汇)创建关于源/汇
  3. Source写入Sink,然后关闭InputStream

如果您知道服务器需要什么作为contentType,那么就硬编码它。因为即使文件是application/octet-stream,服务器也只能接受video/mp4Uri到contentType

检查是否为Uri到contentLength,如果没有找到contentLength,则在上传时不会追加Content-Length: X头。

代码语言:javascript
运行
复制
/** It supports file/content/mediaStore/asset URIs. asset not tested */
fun createAssetFileDescriptor() = try {
    contentResolver.openAssetFileDescriptor(this, "r")
} catch (e: FileNotFoundException) {
    null
}

/** It supports file/content/mediaStore URIs. Will not work with providers that return sub-sections of files */
fun createParcelFileDescriptor() = try {
    contentResolver.openFileDescriptor(this, "r")
} catch (e: FileNotFoundException) {
    null
}

/** - It supports file/content/mediaStore/asset URIs. asset not tested
 * - When file URI is used, may get contentLength error (expected x but got y) error when uploading if contentLength header is filled from assetFileDescriptor.length */
fun createInputStreamFromContentResolver() = try {
    contentResolver.openInputStream(this)
} catch (e: FileNotFoundException) {
    null
}

fun Uri.asRequestBody(contentResolver: ContentResolver,
                      contentType: MediaType? = null,
                      contentLength: Long = -1L)
        : RequestBody {

    return object : RequestBody() {
        /** If null is given, it is binary for Streams */
        override fun contentType() = contentType

        /** 'chunked' transfer encoding will be used for big files when length not specified */
        override fun contentLength() = contentLength

        /** This may get called twice if HttpLoggingInterceptor is used */
        override fun writeTo(sink: BufferedSink) {
            val assetFileDescriptor = createAssetFileDescriptor()
            if (assetFileDescriptor != null) {
                // when InputStream is closed, it auto closes AssetFileDescriptor
                AssetFileDescriptor.AutoCloseInputStream(assetFileDescriptor)
                        .source()
                        .use { source -> sink.writeAll(source) }
            } else {
                val inputStream = createInputStreamFromContentResolver()
                if (inputStream != null) {
                    inputStream
                            .source()
                            .use { source -> sink.writeAll(source) }
                } else {
                    val parcelFileDescriptor = createParcelFileDescriptor()
                    if (parcelFileDescriptor != null) {
                        // when InputStream is closed, it auto closes ParcelFileDescriptor
                        ParcelFileDescriptor.AutoCloseInputStream(parcelFileDescriptor)
                                .source()
                                .use { source -> sink.writeAll(source) }
                    } else {
                        throw IOException()
                    }
                }
            }
        }
    }
}

用法:

代码语言:javascript
运行
复制
val request = uri.asRequestBody(
       contentResolver = context.contentResolver,
       contentType = "application/octet-stream".toMediaTypeOrNull(),
       contentLength = uri.length(context.contentResolver)
)
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45123696

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档