Ошибка чтения: EBADF (неверный файловый дескриптор) при чтении из InputStream Nougat

С недавними изменениями в Android N мне пришлось обновить свой код, чтобы использовать FileProvider для извлечения изображений/файлов с помощью диспетчера камеры/файлов. Код отлично работает в эмуляторе (genymotion), но выдает исключение ввода-вывода в Moto G4 plus.

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$10.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$10.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. Взял все разрешения и работал соответственно.


person Debanjan    schedule 13.06.2017    source источник
comment
Это извлечение данных из общедоступного или частного хранилища? У вас есть соответствующие разрешения?   -  person MeetTitan    schedule 13.06.2017
comment
uriOrig is the uri returned from camera -- камеры не возвращают Uri значений. Пожалуйста, покажите, откуда uriOrig.   -  person CommonsWare    schedule 13.06.2017
comment
@MeetTitan, я отредактировал вопрос, добавив часть uri. Извините, может быть, я не был описательным ранее.   -  person Debanjan    schedule 14.06.2017
comment
@CommonsWare Я получил все разрешения, как было предложено. Заливается часть файла, после чего вдруг останавливается с ошибкой.   -  person Debanjan    schedule 14.06.2017
comment
Этот mImageCaptureUri позже упоминается как uriOrig — я не понимаю, как это сделать. Более того, было бы проще и быстрее просто сохранить extFile и использовать прямой файловый ввод-вывод.   -  person CommonsWare    schedule 14.06.2017


Ответы (3)


Проблема с 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);

сделал работу.

person Debanjan    schedule 10.11.2017
comment
Применимо и к OutputStream. - person Irfan Latif; 06.10.2020
comment
Не могли бы вы предоставить источник, который подтверждает, что ParcelFileDescriptor закрывает поток? Я пытаюсь использовать его, чтобы избежать запросов на InputStream снова и снова. - person Minh Nghĩa; 11.01.2021

У меня была аналогичная проблема при записи и чтении из разных файлов и в них, я закрыл: канал ввода/вывода файла и поток ввода/вывода файла, НО я забыл закрыть связанный дескриптор файла актива/участка в конце. Как только я также закрыл дескриптор файла актива/участка, в конце эти случайные исключения дескриптора плохого файла исчезли.

person becke-ch    schedule 24.08.2020

Это поразило меня, когда я обновлял индикатор выполнения пользовательского интерфейса слишком часто, чем требуется, при загрузке файлов во внешнее хранилище. Я ослабил обновления пользовательского интерфейса из моего потока загрузки (asyncTask), обновляя ход выполнения по таймеру и увеличивая его только на 1%.

person DarthJaiz    schedule 22.10.2018