joltup / rn-fetch-blob

A project committed to making file access and data transfer easier, efficient for React Native developers.
MIT License
2.81k stars 772 forks source link

Can't open pdf after download #650

Open mMarcos208 opened 3 years ago

mMarcos208 commented 3 years ago

Configurations of my pdf

CONTENT-TYPE | application/octet-stream

My code

const downloadPdf = (uri: string, nome: string): void => {
    setLoading(true)
    const {config, fs} = RNFetchBlob
    const PictureDir = fs.dirs.DownloadDir
    const options = {
      fileCache: true,
      addAndroidDownloads: {
        useDownloadManager: true,
        notification: true,
        title: nome,
        path: `${PictureDir}/${nome}`,
      },
      appendExt: 'pdf',
    }
    config(options)
      .fetch('GET', uri, {
        // Accept: 'application/json',
        // 'Content-Type': 'multipart/form-data,octet-stream',
        'Content-Type': 'multipart/form-data',
      })
      .then((_) => {
        Snackbar.show({
          text: 'Download realizado com sucesso!',
          backgroundColor: colors.silverTree,
        })
      })
      .catch((_) => {
        Snackbar.show({
          text: 'Error ao realizar download!',
          backgroundColor: colors.valencia,
        })
      })
      .finally(() => setLoading(false))
  }
leonardoballand commented 3 years ago

Só falta seu código pra abrir aquele file, né?

adaerodriguez commented 3 years ago

@mMarcos208 You need to add code to open file. E.g.:

RNFetchBlob
    .config({ ...defaultOptions, ...options })
    .fetch('GET', url, {
      // Headers...
    })
    .progress((received, total) => {
      Console.log('progress', received / total);
    })
   .then((res) => {
      // The temp file path
      Console.log('File:', res);
      Console.log('The file saved to:', res.path());

      if (Platform.OS === 'android') {
        RNFetchBlob.android.actionViewIntent(res.path(), mimeType || 'application/pdf');
      }

      if (Platform.OS === 'ios') {
        RNFetchBlob.ios.previewDocument(res.path());
      }
    });
LouisJS commented 3 years ago

Hi @adaerodriguez,

I'm doing exactly this in my app. It works well on iOS ! However, on Android, it goes to a black screen on

RNFetchBlob.android.actionViewIntent(res.path(), mimeType || 'application/pdf');

And come back to the app. If i touch my notification on my phone, it redirects me to the file, which has been successfully downloaded on my phone.

Would you know what causes this issue ?

RN: 0.63.2 RN-Fetch-Blob: 0.12.0

adaerodriguez commented 3 years ago

Hi @LouisJS, is possible you forgot some parameter in the config? or do you forgot to ask for permissions? This is my config:

const options = Platform.select({
    android: {
      // appendText: fileType || defaultFileType, // if you need file extension
      path: `${DownloadDir}/${name}`,
      /** With Download manger */
      // addAndroidDownloads: {
      //   path: `${DownloadDir}/${name}`,
      //   useDownloadManager: true,
      //   mediaScannable: true,
      //   notification: true,
      //   overwrite: true,
      //   // Optional, but recommended since android DownloadManager will fail when
      //   // the url does not contains a file extension, by default the mime type will be text/plain
      //   // mime: 'text/plain',
      //   description: strings('files.downloadByManager'),
      // },
    },
    ios: {
      // appendText: fileType || defaultFileType, // if you need file extension
      path: `${DocumentDir}/${name}`,
      // IOSBackgroundTask: true,
      // IOSUploadTask: true,
    },
  });

if (Platform.OS === 'android') {
    getPermissionWriteExternalStorage(
      (status, tag) => { Console.log('getPermissionWriteExternalStorage - status:', status, ' | tag: ', tag); },
      () => { downloadFile(url, options, fileType, callbackSuccess); },
    );
  }

  if (Platform.OS === 'ios') {
    downloadFile(url, options, fileType, callbackSuccess);
  }

My permission method is:

export const getPermissionWriteExternalStorage = async (callbackError, callbackSuccess) => {
  const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;
  const permissionTag = Permission.PERMISSION_WRITE_EXTERNAL_STORAGE;

  try {
    const status = await PermissionsAndroid.request(permission);
    permissionResult(status, permissionTag, callbackError, callbackSuccess);
  } catch (err) {
    Console.warn(err);
  }
};

And my defaultConfig that I have in RNFetchBlob:

const defaultOptions = {
    fileCache: true,
    timeout: 1000 * 15, // 15 seconds
  };

I hope that my code could help you :)

LouisJS commented 3 years ago

I do ask for permission

const grantPermission = async () => {
    if (Platform.OS === 'android') {
      return requestMultiple([
        PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE,
        PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE,
      ]);
    } else {
      return request(PERMISSIONS.IOS.WRITE_EXTERNAL_STORAGE);
    }
  };

And the file is downloaded on my phone. I can open it then via the notification :/

adaerodriguez commented 3 years ago

Maybe your path is wrong? The path for iOS and Android is differente. Look at my config

const { dirs: { DownloadDir, DocumentDir } } = RNFetchBlob.fs;

LouisJS commented 3 years ago

The file is well downloaded. The thing i can't do is open it with RNFetchBlob.android.actionViewIntent

adaerodriguez commented 3 years ago

Only to discard mistakes. Could you see if you have the correct permissions selected into Settings > Apps > "Your app" > Permissions. Are you using the "Download Manager" ?

LouisJS commented 3 years ago

Here is a demo of what happens.

It tries to open the file. But come back to the app immediately after that.

adaerodriguez commented 3 years ago

I see.... You are using Download Manager as well. Before I used Download Manager as you can see in my config and it works perfectly. In my opinion, there is some option that you haven't put in your config. Test this options in your code:

const options = Platform.select({
    android: {
    /** With Download manger */
      addAndroidDownloads: {
         path: `${DownloadDir}/${name}`,
         useDownloadManager: true,
         mediaScannable: true,
         notification: true,
         overwrite: true,
         // Optional, but recommended since android DownloadManager will fail when
         // the url does not contains a file extension, by default the mime type will be text/plain
         // mime: 'text/plain',
        //   description: strings('files.downloadByManager'),
       },
});
LouisJS commented 3 years ago

I've tried with this image so we can try with the same file ;)

My Code is this exactly this one

const downloadFile = async () => {
  const {
    dirs: { DownloadDir, DocumentDir },
  } = RNFetchBlob.fs;

  const name = 'airplane.png';

  const options = Platform.select({
    android: {
      /** With Download manger */
      addAndroidDownloads: {
        path: `${DownloadDir}/${name}`,
        title: name,
        useDownloadManager: true,
        mediaScannable: true,
        notification: true,
        overwrite: true,
      },
    },
    ios: {
      path: `${DocumentDir}/${name}`,
    },
  });

  return RNFetchBlob.config({ ...options })
    .fetch('GET', 'https://homepages.cae.wisc.edu/~ece533/images/airplane.png')
    .then((res) => {
      // The temp file path
      console.log('File:', res);
      console.log('The file saved to:', res.path());

      if (Platform.OS === 'android') {
        RNFetchBlob.android.actionViewIntent(res.path(), 'image/png');
      }

      if (Platform.OS === 'ios') {
        RNFetchBlob.ios.previewDocument(res.path());
      }
    });
};

So this time it does not go back directly to the app, but it infinitely loads the content. Right after that, i can tap my notification and see my file instantaneously

You can preview the result here

olup commented 3 years ago

Same here, it opens the pdf app for a split second but comes back directly to my app. Pressing the notification or accessing through the file app works fine tho...

kaayru commented 3 years ago

Same issue here, any update?

Here's my code :

return RNFetchBlob.config({
    path,
    addAndroidDownloads: {
      useDownloadManager: true,
      path,
      notification: true,
      title: notificationTitle,
      mime: 'application/pdf',
      mediaScannable: true,
    },
  })
    .fetch('GET', apiUrl)
    .then((res) => {
      if (Platform.OS === 'android') {
        RNFetchBlob.android.actionViewIntent(res.path(), 'application/pdf');
      } else {
        RNFetchBlob.ios.openDocument(res.path());
      }

      return res;
    });

Before this I ask READ and WRITE permission on Android :

PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE
PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE

And I have the following in AndroidManifest :

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>

Thanks,

gorbatenkov commented 3 years ago

Was able to fix it by adding android:requestLegacyExternalStorage="true" to my manifest file. Based on this https://medium.com/@sriramaripirala/android-10-open-failed-eacces-permission-denied-da8b630a89df

maxowy commented 3 years ago

Was able to fix it by adding android:requestLegacyExternalStorage="true" to my manifest file. Based on this https://medium.com/@sriramaripirala/android-10-open-failed-eacces-permission-denied-da8b630a89df

Confirmed, works like a charm 👍

tanapon395 commented 3 years ago

I'm fix it by

  1. Add below permissions in AndroidManifest.xml
  2. To use downloadmanager, add below action in intent in AndroidManifest.xml
  3. import PermissionsAndroid, Alert from react native (android only)

Credit : https://stackoverflow.com/questions/56887851/react-native-download-pdf-file-with-rn-fetch-blob-on-click-a-button?answertab=votes#tab-top

kv77724-dot commented 2 years ago

In my case using mime type in config object made it work. RNFetchBlob.config({ path, addAndroidDownloads: { useDownloadManager: true, path, notification: true, title: notificationTitle, mime: 'application/pdf', mediaScannable: true, },

cirilojesus commented 11 months ago

A mi me srivio este codigo tanto en Ios y Android

const { config, fs } = RNFetchBlob;
config({
    fileCache: true,
    addAndroidDownloads: {
        useDownloadManager: true,
        notification: true,
        path: fs.dirs.PictureDir + '/IU-APP/' + file.name.split('/').pop(),
    },
    path: fs.dirs.DocumentDir + '/' + file.name.split('/').pop()
}).fetch('GET', encodeURI(file.url)).then(async(res) => {
    if (Platform.OS === "ios") RNFetchBlob.ios.previewDocument(res.path());
}).catch(error => {
    showAlert('Error al descargar el documento!', 'danger')
})
Kaung-Htet-Hein commented 5 months ago
const { dirs } = ReactNativeBlobUtil.fs
        const directory = isIos ? dirs.DocumentDir : dirs.DownloadDir

        setIsLoading(true)

        ReactNativeBlobUtil.config({
            addAndroidDownloads: {
                storeInDownloads: true,
                useDownloadManager: true,
                notification: true,
                path: directory + '/' + fileName,
                mime: 'application/pdf',
                description: 'Download contract',
                mediaScannable: true,
                title: fileName,
            },
            path: directory + '/' + fileName,
            fileCache: true,
        })  

in my case, i followed the android permissions rules but i didn't work, then i added title, and it starts working as expected