读取失败:从 InputStream Nougat 读取时 EBADF(错误的文件描述符)
read failed: EBADF (Bad file descriptor) while reading from InputStream Nougat
随着最近 android N 的变化,我不得不升级我的代码以使用 FileProvider 通过 camera/file 管理器获取 images/files。
该代码在模拟器(genymotion)中运行良好,但在 Moto G4 plus 中抛出 IO 异常。
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
try {
ParcelFileDescriptor fcd = getContentResolver().openFileDescriptor(uriOrig,"r");// uriOrig is the uri returned from camera
if(fcd != null) {
InputStream inputStream = new FileInputStream(fcd.getFileDescriptor());
uploadCall =RestService.getInstance().uploadFile(mimeType, name, size,
inputStream, defaultResponseCallback);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
这里是上传文件的方法
public Call<Attachment> uploadFile(final String mimeType, final String name, final long length,final InputStream is, final Callback<Attachment> callback) {
final int byteCount = 8192;
if (length > 0) {
RequestBody fileBody = new RequestBody() {
@Override
public MediaType contentType() {
return !TextUtils.isEmpty(mimeType) ? MediaType.parse(mimeType) : null;
}
@Override
public long contentLength() throws IOException {
return length;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
final long fileLength = contentLength();
byte[] buffer = new byte[byteCount];
long uploaded = 0;
try {
final Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (callback instanceof Callback2) {
Callback2 callback2 = (Callback2) callback;
long uploaded = (long) msg.obj;
callback2.onProgress(uploaded, fileLength);
}
return true;
}
});
int read;
while ((read = is.read(buffer)) != -1) {
uploaded += read;
sink.write(buffer, 0, read);
Log.d("write: ", "bytes: "+ new String(buffer));
Log.e("writeTo: ", uploaded + " up fd->");
// update progress on UI thread
handler.sendMessage(handler.obtainMessage(0, uploaded));
}
is.close();
} catch (IOException e) {
Log.e("file_upload", "Exception thrown while uploading", e);
}
Log.e("sink: "+uploaded + " bytes ", sink.toString(), new Exception("check"));
}
};
String fileName = name;
int index = name.lastIndexOf(".");
if (index < 0) {
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
fileName += "." + extension;
}
MultipartBody.Part part = MultipartBody.Part.createFormData("file", fileName, fileBody);
Call<Attachment> call = companyRestInterface.uploadFile(part);
call.enqueue(callback);
return call;
}
}
堆栈跟踪
java.io.IOException: read failed: EBADF (Bad file descriptor)
at libcore.io.IoBridge.read(IoBridge.java:481)
at java.io.FileInputStream.read(FileInputStream.java:252)
at java.io.FileInputStream.read(FileInputStream.java:223)
at xend.app.http.RestService.writeTo(RestService.java:767)
at okhttp3.MultipartBody.writeOrCountBytes(MultipartBody.java:173)
at okhttp3.MultipartBody.writeTo(MultipartBody.java:114)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:62)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at xend.app.http.MyInterceptor.intercept(MyInterceptor.java:51)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: android.system.ErrnoException: read failed: EBADF (Bad file descriptor)
at libcore.io.Posix.readBytes(Native Method)
at libcore.io.Posix.read(Posix.java:169)
at libcore.io.BlockGuardOs.read(BlockGuardOs.java:231)
at libcore.io.IoBridge.read(IoBridge.java:471)
at java.io.FileInputStream.read(FileInputStream.java:252)
at java.io.FileInputStream.read(FileInputStream.java:223)
at xend.app.http.RestService.writeTo(RestService.java:767)
at okhttp3.MultipartBody.writeOrCountBytes(MultipartBody.java:173)
at okhttp3.MultipartBody.writeTo(MultipartBody.java:114)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:62)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at xend.app.http.MyInterceptor.intercept(MyInterceptor.java:51)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
非常感谢任何帮助。
编辑
图片uri部分。
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File externalCacheDir = getExternalCacheDir();
File extFile = new File(externalCacheDir,
String.valueOf(System.currentTimeMillis()) + ".jpg");
mImageCaptureUri = FileProvider.getUriForFile(DirectChatActivity.this,
BuildConfig.APPLICATION_ID + ".provider",extFile);
grantUriPermission("xend.app", mImageCaptureUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
cameraIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
cameraIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
此 mImageCaptureUri 后来称为 uriOrig。
获得所有权限并相应地工作。
问题出在 ParcelFileDescriptor 上。它关闭输入流。所以更换行
ParcelFileDescriptor fcd = getContentResolver().openFileDescriptor(uriOrig,"r");// uriOrig is the uri returned from camera
if(fcd != null) {
InputStream inputStream = new FileInputStream(fcd.getFileDescriptor());
uploadCall =RestService.getInstance().uploadFile(mimeType, name, size,
inputStream, defaultResponseCallback);
}
和
InputStream inputStream = getContentResolver().openInputStream(uriOrig);
uploadCall =RestService.getInstance().uploadFile(mimeType, name, size,
inputStream, defaultResponseCallback);
完成了工作。
当下载文件到外部存储时更新 UI 进度条过于频繁,这让我很吃惊。我放宽了下载线程 (asyncTask) 的 UI 更新,方法是按计时器更新进度,仅增加 1%。
我在写入和读取不同的文件时遇到了类似的问题,我关闭了:文件 Input/Output 频道和文件 Input/Output 流但是我忘了关闭关联的 Asset/Parcel 文件描述符在末尾。一旦我也关闭了最后的 Asset/Parcel 文件描述符,这些随机的“错误文件描述符”异常就消失了。
ParcelFileDescriptor
在范围结束前完成,它关闭了流。
使用 ParcelFileDescriptor.AutoCloseInputStream
将解决您的问题,它拥有 ParcelFileDescriptor
.
的强大参考
随着最近 android N 的变化,我不得不升级我的代码以使用 FileProvider 通过 camera/file 管理器获取 images/files。 该代码在模拟器(genymotion)中运行良好,但在 Moto G4 plus 中抛出 IO 异常。
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
try {
ParcelFileDescriptor fcd = getContentResolver().openFileDescriptor(uriOrig,"r");// uriOrig is the uri returned from camera
if(fcd != null) {
InputStream inputStream = new FileInputStream(fcd.getFileDescriptor());
uploadCall =RestService.getInstance().uploadFile(mimeType, name, size,
inputStream, defaultResponseCallback);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
这里是上传文件的方法
public Call<Attachment> uploadFile(final String mimeType, final String name, final long length,final InputStream is, final Callback<Attachment> callback) {
final int byteCount = 8192;
if (length > 0) {
RequestBody fileBody = new RequestBody() {
@Override
public MediaType contentType() {
return !TextUtils.isEmpty(mimeType) ? MediaType.parse(mimeType) : null;
}
@Override
public long contentLength() throws IOException {
return length;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
final long fileLength = contentLength();
byte[] buffer = new byte[byteCount];
long uploaded = 0;
try {
final Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (callback instanceof Callback2) {
Callback2 callback2 = (Callback2) callback;
long uploaded = (long) msg.obj;
callback2.onProgress(uploaded, fileLength);
}
return true;
}
});
int read;
while ((read = is.read(buffer)) != -1) {
uploaded += read;
sink.write(buffer, 0, read);
Log.d("write: ", "bytes: "+ new String(buffer));
Log.e("writeTo: ", uploaded + " up fd->");
// update progress on UI thread
handler.sendMessage(handler.obtainMessage(0, uploaded));
}
is.close();
} catch (IOException e) {
Log.e("file_upload", "Exception thrown while uploading", e);
}
Log.e("sink: "+uploaded + " bytes ", sink.toString(), new Exception("check"));
}
};
String fileName = name;
int index = name.lastIndexOf(".");
if (index < 0) {
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
fileName += "." + extension;
}
MultipartBody.Part part = MultipartBody.Part.createFormData("file", fileName, fileBody);
Call<Attachment> call = companyRestInterface.uploadFile(part);
call.enqueue(callback);
return call;
}
}
堆栈跟踪
java.io.IOException: read failed: EBADF (Bad file descriptor)
at libcore.io.IoBridge.read(IoBridge.java:481)
at java.io.FileInputStream.read(FileInputStream.java:252)
at java.io.FileInputStream.read(FileInputStream.java:223)
at xend.app.http.RestService.writeTo(RestService.java:767)
at okhttp3.MultipartBody.writeOrCountBytes(MultipartBody.java:173)
at okhttp3.MultipartBody.writeTo(MultipartBody.java:114)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:62)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at xend.app.http.MyInterceptor.intercept(MyInterceptor.java:51)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: android.system.ErrnoException: read failed: EBADF (Bad file descriptor)
at libcore.io.Posix.readBytes(Native Method)
at libcore.io.Posix.read(Posix.java:169)
at libcore.io.BlockGuardOs.read(BlockGuardOs.java:231)
at libcore.io.IoBridge.read(IoBridge.java:471)
at java.io.FileInputStream.read(FileInputStream.java:252)
at java.io.FileInputStream.read(FileInputStream.java:223)
at xend.app.http.RestService.writeTo(RestService.java:767)
at okhttp3.MultipartBody.writeOrCountBytes(MultipartBody.java:173)
at okhttp3.MultipartBody.writeTo(MultipartBody.java:114)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:62)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at xend.app.http.MyInterceptor.intercept(MyInterceptor.java:51)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
非常感谢任何帮助。
编辑
图片uri部分。
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File externalCacheDir = getExternalCacheDir();
File extFile = new File(externalCacheDir,
String.valueOf(System.currentTimeMillis()) + ".jpg");
mImageCaptureUri = FileProvider.getUriForFile(DirectChatActivity.this,
BuildConfig.APPLICATION_ID + ".provider",extFile);
grantUriPermission("xend.app", mImageCaptureUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
cameraIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
cameraIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
此 mImageCaptureUri 后来称为 uriOrig。 获得所有权限并相应地工作。
问题出在 ParcelFileDescriptor 上。它关闭输入流。所以更换行
ParcelFileDescriptor fcd = getContentResolver().openFileDescriptor(uriOrig,"r");// uriOrig is the uri returned from camera
if(fcd != null) {
InputStream inputStream = new FileInputStream(fcd.getFileDescriptor());
uploadCall =RestService.getInstance().uploadFile(mimeType, name, size,
inputStream, defaultResponseCallback);
}
和
InputStream inputStream = getContentResolver().openInputStream(uriOrig);
uploadCall =RestService.getInstance().uploadFile(mimeType, name, size,
inputStream, defaultResponseCallback);
完成了工作。
当下载文件到外部存储时更新 UI 进度条过于频繁,这让我很吃惊。我放宽了下载线程 (asyncTask) 的 UI 更新,方法是按计时器更新进度,仅增加 1%。
我在写入和读取不同的文件时遇到了类似的问题,我关闭了:文件 Input/Output 频道和文件 Input/Output 流但是我忘了关闭关联的 Asset/Parcel 文件描述符在末尾。一旦我也关闭了最后的 Asset/Parcel 文件描述符,这些随机的“错误文件描述符”异常就消失了。
ParcelFileDescriptor
在范围结束前完成,它关闭了流。
使用 ParcelFileDescriptor.AutoCloseInputStream
将解决您的问题,它拥有 ParcelFileDescriptor
.