16

With the recent changes in android N, I had to upgrade my code to use FileProvider to fetch images/files using camera/file manager. The code is working fine in emulator(genymotion) but throwing IO exception in 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();
                }
            }

Here is the upload file method.

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;
    }
}

The stack trace

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) 

Any help would be very much appreciated.

EDIT

The image uri part.

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);

This mImageCaptureUri is later referred as uriOrig. Taken all permissions and have worked accordingly.

Debanjan
  • 2,817
  • 2
  • 24
  • 43
  • Is this pulling data from public or private storage? Do you have adequate permissions? – MeetTitan Jun 13 '17 at 19:39
  • `uriOrig is the uri returned from camera` -- cameras do not return `Uri` values. Please show where `uriOrig` is coming from. – CommonsWare Jun 13 '17 at 19:41
  • @MeetTitan, I have edited the question to add the uri part. Sorry, may be I was not descriptive earlier. – Debanjan Jun 14 '17 at 03:32
  • @CommonsWare I have taken all the permissions, as suggested. Part of the file is being uploaded, after that it suddenly stops with the error. – Debanjan Jun 14 '17 at 03:32
  • "This mImageCaptureUri is later referred as uriOrig" -- I do not see how. Moreover, it would be simpler and faster to just hold onto `extFile` and use direct file I/O. – CommonsWare Jun 14 '17 at 10:01

4 Answers4

18

The problem is with ParcelFileDescriptor. It closes the input stream. So replacing the line

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);
                    }

with

InputStream inputStream = getContentResolver().openInputStream(uriOrig);
uploadCall =RestService.getInstance().uploadFile(mimeType, name, size,
                                inputStream, defaultResponseCallback);

did the work.

Debanjan
  • 2,817
  • 2
  • 24
  • 43
4

ParcelFileDescriptor has finalized before the end of the scope, it closed the stream. Using ParcelFileDescriptor.AutoCloseInputStream will solve your problem, it holds the strong reference of the ParcelFileDescriptor.

Ole Pannier
  • 3,208
  • 9
  • 22
  • 33
Neden Wang
  • 41
  • 1
  • 2
    Hi Naden, Please do post a code snippet along with your answer, so that devs find your answer more considerable. – Debanjan Aug 13 '21 at 04:28
  • @Neden Could you please share some code – Nainal Sep 23 '21 at 11:21
  • 1
    Instead of `InputStream inputStream = new FileInputStream(fcd.getFileDescriptor());`, we can use `InputStream inputStream = ParcelFileDescriptor.AutoCloseInputStream(fcd)` – TGruenwald Jul 20 '22 at 17:15
3

I had a similar issue while writing and reading from and to different files, I closed the: File Input/Output Channel and File Input/Output Stream BUT I forgot to close the associated Asset/Parcel File Descriptor at the end. Once I closed as well the Asset/Parcel File Descriptor at the end these random "Bad File Descriptor" exceptions were gone.

becke-ch
  • 413
  • 4
  • 8
0

This hit me when updating UI progress bar too often than required while downloading files to external storage. I relaxed the UI updates from my download thread (asyncTask) by updating progress on a timer basis and by 1% percent increase only.

DarthJaiz
  • 29
  • 3