Search code examples
androidretrofitokhttp

How to write ResponseBody (in my case PDF) to external storage? Problem with java.lang.IllegalStateException: closed


I want to write a ResponseBody to external storage(Download folder). Until just a week ago, everything was working perfectly, then I needed to update the okhttp3 version and now when I run my application, one of these scenarios happened:

This is my retrofit:

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(logging)
            .connectTimeout(2, TimeUnit.MINUTES)
            .writeTimeout(2, TimeUnit.MINUTES)
            .readTimeout(2, TimeUnit.MINUTES)
            .build();


    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(link)
            .addConverterFactory(ScalarsConverterFactory.create())
            .addConverterFactory(create())
            .client(client)
            .build();

This is my function for writing ResponseBody to ES:

private boolean writeResponseBodyToDisk(ResponseBody body) {
    File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "test.pdf");
    try {
        InputStream fis = body.byteStream();
        long length = body.bytes().length;
        byte[] bytes = new byte[(int) length];
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length && (numRead = fis.read(bytes, offset, bytes.length - offset)) >= 0) {
            offset += numRead;
        }

        OutputStream op = new FileOutputStream(dir);
        op.write(bytes);
    } catch (Exception ex) {
        Toast.makeText(MainActivity.this, ex.getMessage().toString(), Toast.LENGTH_LONG);
    }
    return true;
}

When i put this part like this:

long length = body.bytes().length;

I get this exception:

java.lang.IllegalStateException: closed

But when I leave It like it was before version update:

long length = body.contentLength();

Then nothing happens. This way was working perfectly until I updated the version of okhttp3.

I know that I can't use the first option with bytes.length, but I don't know any alternative.


Solution

  • just change the initialization of the outputstream

        OutputStream op;
    
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
    
            ContentValues values = new ContentValues();
            values.put(MediaStore.Files.FileColumns.DISPLAY_NAME,"test.pdf");
            values.put(MediaStore.Files.FileColumns.MIME_TYPE,"application/pdf");
            values.put(MediaStore.Files.FileColumns.RELATIVE_PATH,Environment.DIRECTORY_DOWNLOADS);
    
    
            Uri fileUri = getContentResolver().insert(
                    MediaStore.Files.getContentUri("external"),
                    values
            );
    
            try {
                op = getContentResolver().openOutputStream(fileUri);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
    
        }else{
            File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "test.pdf");
            try {
                op = new FileOutputStream(dir);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        try {
            InputStream fis = body.byteStream();
            long length = body.bytes().length;
            byte[] bytes = new byte[(int) length];
            int offset = 0;
            int numRead = 0;
            while (offset < bytes.length && (numRead = fis.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += numRead;
            }
            
            op.write(bytes);
        } catch (Exception ex) {
            Toast.makeText(MainActivity.this, ex.getMessage().toString(), Toast.LENGTH_LONG);
        }