请看我下面的代码,我用来上传一个视频到服务器。然而,对于足够大的视频,我得到了一个OutOfMemory异常。
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吗?
发布于 2017-07-16 00:39:05
您可以创建一个自定义RequestBody
来流数据。您必须注意:由于OkHttp可能决定重试请求,所以它可能会被多次重用。确保每次都可以重新打开InputStream
。
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();
}
});
发布于 2021-04-25 09:57:32
Uri
/ ContentResolver
/ ParcelFileDescriptor
之一将AssetFileDescriptor
转换为InputStream
okio.Source
InputStream
(关于源/汇)创建关于源/汇Source
写入Sink
,然后关闭InputStream
如果您知道服务器需要什么作为contentType
,那么就硬编码它。因为即使文件是application/octet-stream
,服务器也只能接受video/mp4
。Uri到contentType
检查是否为Uri到contentLength,如果没有找到contentLength,则在上传时不会追加Content-Length: X
头。
/** 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()
}
}
}
}
}
}
用法:
val request = uri.asRequestBody(
contentResolver = context.contentResolver,
contentType = "application/octet-stream".toMediaTypeOrNull(),
contentLength = uri.length(context.contentResolver)
)
https://stackoverflow.com/questions/45123696
复制相似问题