碰到此问题请先看:Android Q:文件上传(之前的文章有点问题)因为此问题的根本原因我没分析出来,怀疑也是文件过大然后直接读取的时候失败了,无效的话,再看此篇吧。
写这篇博客主要想记录一下这个问题,我觉得大家应该都不会碰到。
在我之前的一篇文章Android Q:上传图片java.io.FileNotFoundException: open failed: EACCES (Permission denied) 中有说到,新安卓版本下,使用公共文件资源不能够File file = new File(path)了,需要使用FD,也就是FileDescriptor。现在,我这边碰到了一个非常非常奇葩的问题,就如标题所示:
IOException read fail:EBADF (Bad file descriptor)
这个错误,是try-catch出来,然后Toast到屏幕上,截图下来的。是不是这套操作,很奇怪,为什么我不debug?
这个错误,在debug环境下的安装包,是不会报错的,在release安装包下,会报错。在测试机上会报错,在我手机findx上不报错。在上一个项目中用到,没报过这个错误,在这个项目后期,碰到了这个错误。
非常奇葩,根本想不通,为什么同一套代码,连接口地址都一样,release包就报错了。
我把功能代码整合了一下,放上来
//现在假定,已经获取到了一个资源的Uri 并且要上传
private void update() throws IOException {
ParcelFileDescriptor parcelFileDescriptor = getApplication.getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
FileInputStream fis = new FileInputStream(fileDescriptor);
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[1024*4]; //buff用于存放循环读取的临时数据
int rc = 0;
while ((rc = fis.read(buff, 0, 100)) > 0) {
swapStream.write(buff, 0, rc);
}
MultipartBody.Builder builder = new MultipartBody.Builder()
.setType(MultipartBody.FORM);//表单类型
RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"), swapStream.toByteArray());//表单类型
builder.addFormDataPart("file", "xxx.mp4", body);
Retrofit.getApi.update(builder.build().parts())
.compose(做一些切换线程的操作,和数据处理)
.subscribe(new Observe{
//成功 失败回调
});
}
我那边分好几个文件调用,但是整套流程大致就是这样,获取文件byte[],构建formData文件Retrofit上传,调用接口。
调用的时候,你就需要try-catch,然后release环境下,我这边就报错了。
直接说解决办法吧,因为我也不知道为什么会报错,有知道的大佬可以提点一下我,由于我用的MVVM框架,而ViewModel中是不能持有activity的context的,但是可以有application,因此在使用getContentResolver(),我用的是application。当时我是把这代码放在了MainActivity中测试,发现居然不报错,成功上传了,对比下,只有在获取getContentResolver()这个时候不一样。
我把所有getContentResolver的地方都使用了activity的context,就没有报这个错误了。就是代码结构变化比较大,需要把获取ParcelFileDescriptor放在View模块执行,然后再到ViewModel中转化成byte[]。