Code Monkey home page Code Monkey logo

Comments (75)

RonRadtke avatar RonRadtke commented on June 21, 2024 9

Just wanting to give you a short update.
I have the very basics working now (creating and writing data to media files), but there is still a lot of work to do.
Especially to be able to use fileCache and such things.

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 8

@damian-balas
I tested the code in my companies app, works fine for saving a PDF to the download folder. But I'm not yet using it in production since I don't have the need for it yet.
In general all errors should be caught and lead to a promise rejection as usual.
So I think it should be save to use for that. But I would ofc recommend testing that it works as you expect.

I don't know yet. It depends a bit on how much of the additional things I program.
I would say I skip the part about moving / renaming / ... for a first release. Any objections?

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 5

Hi @anshul0410,
at the moment there is no possibility.
But I am currently working on integrating it in the lib. It's the next feature for the lib and it is already in work :)
But I don't know when it will be done yet

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 4

@alariej yep, is planned for this or next week :)

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024 3

@RonRadtke Is there any workaround until the solution is fully implemented? Our app needs invoices to be downloaded in a "common", non-app specific folder, like "Downloads" or "Documents" and we are not sure how we can implement this without your upcoming solution. Thanks!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 3

@afilp in general it is working.
The basics are done. So saving a file to downloads works.

But I had / have plenty of private issues eating up my time the last moths. So I didn't get to do the work on all the nice-to-have and testing like I would want to in order to make a release for it.

What doesn't work yet:

  • opening the file after saving it
  • storing the file without using fileCache
  • all the functionality around storing files like renaming / moving / copy /delete / ....

If you change to the feature/media_collections branch you could save a file by using the following code snippet.
It's just copied from my testing app, so you might have to adjust variable names etc :)

    ReactNativeBlobUtil
            .config({
                fileCache: true
            })
            .fetch('GET', targeturi, {'Accept': 'application/octet-stream'}, JSON.stringify(dat))
            .then(async (res) => {
                let pathnew = await ReactNativeBlobUtil.MediaCollection.createMediafile({name: filename, parentFolder: '', mimeType: get_mimetype_for_file(filename)}, 'Download');
                await ReactNativeBlobUtil.MediaCollection.writeToMediafile(pathnew, res.path());
});

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 3

@damian-balas iOS does not use the android mediaStore.
For iOS you need

let destPath = ReactNativeBlobUtil.fs.dirs.DocumentDir + '/' + filename;
ReactNativeBlobUtil
            .config({
                fileCache: true
            })
            .fetch('GET', targeturi, {'Accept': 'application/octet-stream'}, JSON.stringify(dat))
            .then(async (res) => {
                await ReactNativeBlobUtil.fs.appendFile(destPath, res.path(), 'uri');
})

So for now you will have to put in a switch based on Platform.OS==='android'.
Downloaddir on iOS doesn't work since there is no download folder like on android.

from react-native-blob-util.

damian-balas avatar damian-balas commented on June 21, 2024 3

Thanks!
Made it work with this code:

const downloadForIOS = async ({ filename, url }) => {
  const path =
    ReactNativeBlobUtil.fs.dirs.DocumentDir + '/' + filename + '.pdf';
  const response = await ReactNativeBlobUtil.config({
    fileCache: true,
    appendExt: 'pdf',
    path,
  }).fetch('GET', url, {
    Accept: 'application/pdf',
    'Content-Type': 'application/pdf',
  });

  ReactNativeBlobUtil.ios.previewDocument(path);
  return response;
};

But... the user needs to click: Save to Files button :)


Edit 1: Your solution was better :) Thanks!!!

from react-native-blob-util.

damian-balas avatar damian-balas commented on June 21, 2024 2

@RonRadtke Well it would be fine for me if you release a version where we can only download files to that dir. :)

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 2

The lib currently doesn't support the option to save it to files app.
But you can set
UIFileSharingEnabled
and
LSSupportsOpeningDocumentsInPlace
To yes in yours app plist file. (Just take the link below and search for the first entry, there it is explained that itit should work... can't get a working link tonit on the phone sorry,)
Or see https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/uid/TP40009252-SW37

from react-native-blob-util.

jaedontym avatar jaedontym commented on June 21, 2024 2

@RonRadtke I hope your exams went smoothly and your private issues get resolved. Take care, Ron 🙏

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024 1

@RonRadtke Thanks a lot Ron! For me, just being able to download a PDF to the common Downloads or Documents folder would be sufficient.

Do we need to have conditionals before the call or the following code will work? (for both iOS and Android?)

I used to have this code in the main config, so I thought the iOS would be OK because of the path there:
appendExt: 'pdf', fileCache: true, path: destPath,

  let dirs = ReactNativeBlobUtil.fs.dirs;
  const dirToSave = Platform.OS === 'ios' ? dirs.DocumentDir : dirs.DownloadDir;
  console.log('dirToSave', dirToSave);
  const destPath = dirToSave + '/Invoice_' + paymentId + '.pdf';

  // const date = new Date();
  const { data } = await new Promise((resolve, reject) => {
    try {
      ReactNativeBlobUtil.config({
        appendExt: 'pdf',
        fileCache: true,
        path: destPath,
        addAndroidDownloads: {
          path: destPath,
          useDownloadManager: true,
          title: 'Invoice ' + paymentId,
          description: 'Invoice PDF',
          mime: 'application/pdf',
          mediaScannable: true,
          notification: true,
        },
      })
        .fetch('GET', `${window.our.BASE_URL}/payment?paymentId=${paymentId}`, {
          Authorization: `Bearer ${token}`,
          Accept: 'application/pdf',
          'Content-Type': 'application/pdf',
        })
        .progress((received, total) => {
          console.log('progress', received / total);
        })
        .then(async res => {
          console.log('The file saved to ', res.path());
          Alert.alert(t('invoiceDownloaded'));
          if (Platform.OS === 'ios') {
            await ReactNativeBlobUtil.fs.appendFile(
              destPath,
              res.path(),
              'uri',
            );
            ReactNativeBlobUtil.ios.openDocument(res.path());
          } else {
            const android = ReactNativeBlobUtil.android;
            android.actionViewIntent(
              res.path(),
              'application/pdf',
              'Choose app to preview',
            );
          }
          resolve(res);
        })
        // Something went wrong:
        .catch((errorMessage, statusCode) => {
          console.log(errorMessage);
          reject(errorMessage);
        });
    } catch (err) {
      console.log(err);
      reject(err);
    }
  });

Also, I idid not see the progress working, maybe because of the PDF?

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 1

Is is secure to make those files visible?

In general yes. Of course it is your responsibility to make sure there is no info available the user should not be able to access, like config files. Same it is your responsibility to make sure you don't store any malware etc there. But it is in general fine to use, the official developer docu of apple is describing it explicate so we can use it, it's not a hack or sth like that.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024 1

No you misunderstood me. They are 2 different problems.

I still have the major problem of "content" video files not being able to be uploaded to our server, because there is no way at the current master branch to get the actual file from a "content" uri.

My initial asking about saving to Downloads also still remains. I only mentioned the other library as I found it just 2 hours ago and gave it a try in case there is something there that may help you with the implementation/testing of your branch.

Thanks a lot!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 1

@afilp it works, but it might fail. A URI doesn't necessarily has to be a file or anything you can read or use. So it might work for all your cases since you come over the picker. But in general it is something that will sooner or later break.
An other possibility that would likely would work would be to return a BLOB object containing the data of the file - that would skip the copy part too.
But copying will do the cut for now too.
Regardless getting the BLOB or so would be a nice feature in general.

So if you don't have a problem with, I would say we stick with the copying for now. At least that is the fastest way you will be able to use that functionality, since I don't have that much time currently (exams, work, family, ...)
And I will keep working on the mediastore, add a few more functions and then release it officially. But till then, you can use the branch.
Depending on how much time i have in spare I might be able to get the release within the next 2 weeks or so

Edit:
To maybe clarify it ab bit. The content uri is basically a descriptor for a database entry. The stat method is aquering a cursor pointing on the row specified by the uri. And now you're hoping it is an entry for a file, but it could also be a collection of some sort, or what ever else android is storing there. Do it being a file isn't guaranteed and it might fail - or you simply get whatever but a path

from react-native-blob-util.

alariej avatar alariej commented on June 21, 2024 1

Oh you're right, I do have android:requestLegacyExternalStorage="true" in the manifest. So I guess the MediaStore API is the best way to go from now on.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024 1

Sure, here it is:
#108

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024 1

Also, I tried to update the Wiki but I do not have update permissions:

From:
let destpath = ReactNativeBlobUtil.dirs.CacheDir

To:
let destpath = ReactNativeBlobUtil.fs.dirs.CacheDir

(there is a missing fs there, I believe)

Maybe you would like to change this.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024 1

Ι tested it in both my Android phone and tablet and worked perfectly!

Thanks a lot!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 1

@jaedontym sorry I had 2 exams and some private issues so I got kinda delayed.
But I think I got it fixed, I will run some more tests tomorrow and if these are fine I'll craft a new release.

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024 1

@HagarAyad the update I released yesterday might fix your problem too. I found a way to reproduce it and fixed it - but I of course can't promise there isn't anything else wrong (e.g. another app modifying the file or so)
@jaedontym thanks! The function should now work as expected and also work on android < 10. With the next update I plan to bring back the old environment paths that should enable being able to write to the public download folder on old devices agian.
@afilp how's it going? everything working well with that part of the lib?

from react-native-blob-util.

damian-balas avatar damian-balas commented on June 21, 2024

@RonRadtke Thanks for your work!

If I only want to download a PDF to the global downloads dir, it is safe to use the branch version? :)
When do you expect the functionality to go live?

from react-native-blob-util.

damian-balas avatar damian-balas commented on June 21, 2024

@RonRadtke Would you also provide a snippet for iOS? I can't make it work... It gives me success status, but there are no files in the File.app. I need to have it physically on the users phone so he can access it anytime.

from react-native-blob-util.

damian-balas avatar damian-balas commented on June 21, 2024

@damian-balas iOS does not use the android mediaStore. For iOS you need

let destPath = ReactNativeBlobUtil.fs.dirs.DocumentDir + '/' + filename;
ReactNativeBlobUtil
            .config({
                fileCache: true
            })
            .fetch('GET', targeturi, {'Accept': 'application/octet-stream'}, JSON.stringify(dat))
            .then(async (res) => {
                await ReactNativeBlobUtil.fs.appendFile(destPath, res.path(), 'uri');
})

So for now you will have to put in a switch based on Platform.OS==='android'. Downloaddir on iOS doesn't work since there is no download folder like on android.

Thanks for your feedback. I'm aware of this, but my issue is that I can't see the downloaded file in the native File app. It's empty.

this is my code:

export const downloadPdf = async ({ url, filename }) => {
  let response;
  const newFilename = `${filename}-${Date.now()}`;
  if (!url || typeof url !== 'string') {
    throw new Error('Link do pliku PDF jest niepoprawny!');
  }

  try {
    if (Platform.OS === 'ios') {
      response = await downloadForIOS({ filename, url });
    } else {
      response = await downloadForAndroid({ filename, url });
    }

    showSuccessToast({
      text: `${newFilename}.pdf został pobrany pomyślnie!`, // TODO: translate this!
    });
  } catch (error) {
    showErrorToast({
      error: {
        message: `Nie udało się pobrać ${newFilename}.pdf. Spróbuj jeszcze raz.`, // TODO: translate this!
      },
    });
    Logger.error(error);
  } finally {
    if (response?.flush) {
      // response.flush();
    }
  }
};

const downloadForAndroid = async ({ filename, url }) => {
  const result = await request(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);

  if (result !== RESULTS.GRANTED) {
    throw new Error(result);
  }

  const response = await ReactNativeBlobUtil.config({
    fileCache: true,
  }).fetch('GET', url);

  const newPath =
    await ReactNativeBlobUtil.MediaCollection.createMediafile(
      {
        name: `${filename}.pdf`,
        parentFolder: '',
        mimeType: 'application/pdf',
      },
      'Download',
    );

  await ReactNativeBlobUtil.MediaCollection.writeToMediafile(
    newPath,
    response.path(),
  );

  return response;
};

const downloadForIOS = async ({ filename, url }) => {
  const path = ReactNativeBlobUtil.fs.dirs.DocumentDir + '/' + filename;
  const response = await ReactNativeBlobUtil.config({
    fileCache: true,
  }).fetch('GET', url);

  await ReactNativeBlobUtil.fs.appendFile(path, response.path(), 'uri');
  return response;
};

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

@afilp
Sorry just saw your post now.
In general you should be able to use the config, just that the useDownloadManager might make problems, and the rest of the fields would not be respected by the write method for the mediastore.
And ofc you'll have to exchange the methods for writing the file on android.
So for your else part you need a await ReactNativeBlobUtil.fs.appendFile(destPath, res.path(), 'uri');. The actionViewIntent does not work yet, as stated, not sure what the problem is.

I will fix it in a way that the config will be used. But currently I won't have the time for that.

@afilp @damian-balas
And as soon as the config works and the opening of the files works I'll make a release for and by that push the version to 0.14.x
If anyone of you has some time to help / test I would be very glad about it

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

@RonRadtke Thank you for your efforts!

I tried everything and still the file goes to the app's sandboxed storage and not to a common documents folder (note: I am not using your special branch, should I?).
I tried to make it work with your advice to check these 2 properties in plist to true:
image

But still, res.path() and destpath are exactly the same, the app's one:
image

I tried all combinations, even having fileCache: false,, still it does not work.

Also, this code only works once per session, it never works again for the next download:
ReactNativeBlobUtil.ios.openDocument(res.path());

Thanks a lot or all your efforts!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

That's how storage in iOS works.
Apps can only ever write in the sandbox storage.
The only possibility you have is either saving it to the file app as you pointed out.
The option I pointed out (setting the two flags) will make the files browsable by the files app and thus leads to the files show up in the files app. That's basically the only option you have if you don't want the user to have to save it again.

from react-native-blob-util.

damian-balas avatar damian-balas commented on June 21, 2024

That's how storage in iOS works. Apps can only ever write in the sandbox storage. The only possibility you have is either saving it to the file app as you pointed out. The option I pointed out (setting the two flags) will make the files browsable by the files app and thus leads to the files show up in the files app. That's basically the only option you have if you don't want the user to have to save it again.

Is is secure to make those files visible?

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

@RonRadtke Sorry if this is irrelevant, i am using react-native-image-picker, how could I create a video file from this uri inside the same folder with the others? With appendFile? Thank you!

image

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

where did you get the file from?
It looks like it is a file from mediastore. Currently the library can't do anything with such URIs in means of loading the file to store it somewhere else e.g.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

I used react-native-image-picker and I just picked a video file. The type and data are what I get in the response (among other fields that I do not need to upload with the form).

This is the full response I get:

  {
    "height": 320,
    "width": 568,
    "type": "video/mp4",
    "duration": 9,
    "fileName": "video:45544",
    "bitrate": 465939,
    "fileSize": 566583,
    "uri": "content://com.android.providers.media.documents/document/video%3A45544"
  }

I thought I could use the fs functionality to transfer the file (is it a file or a link? What does content mean?) in the same app-specific folder where the images are also copied.

For example, this is where the images are copied after picking them:

{
  "height": 3000,
  "width": 4000,
  "type": "image/jpeg",
  "fileName": "rn_image_picker_lib_temp_5992cea8-94e6-4071-81dc-d1593e1aa237.jpg",
  "fileSize": 4586689,
  "uri": "file:///data/user/0/com.company.app.debug/cache/rn_image_picker_lib_temp_5992cea8-94e6-4071-81dc-d1593e1aa237.jpg"
}

I cannot?

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

No currently not.
The content URI is indicating that it is an uri from a content provider. So it's not a path it is a reference to a certain file.
Basically you need to use a contentResolver and get the InputStream from that one, and with that stream you can write the file in the app specific directory. (Some MediaStore magic)
And that is currently not implemented.

I know who to set a method up to do what you need. But I won't have time to do so until maybe Monday.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

@RonRadtke Thanks a lot for your feedback.

We have an app in the store and Android (only) users cannot upload the "picked" videos, so if we could have a solution by Monday that would be great indeed!

Please note that I am not particularly interested to "copy" the video file to the "app-specific" folder (this was just a suggestion), I am interested for react-native-blob-util/ to just upload the file no matter where it is located, and no matter if it is a "content" or a "file" type. Of course, if intermediate custom steps are needed to be programmed by us (for example copying something somewhere), please let us know what to do and we will do them!

Thanks a lot!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

Well, I can't promise I get it done on Monday. Depends a lot on how much spare time I'll have and how much I have to work / family.
But I'll try.

Of course it would be possible without that intermediate step too. But I would like to have it as a separate module and don't mess the library up too much by having every functionality everywhere.
Besides, on modern phones copying a single file really shouldn't be an issue storage wise.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

Yes Ron, I understand.

FYI, I tried to do an fs.copyFile logic and I have reached a wall...

err Error: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/video:45772 from pid=20415, uid=10641 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs

The code I am using is this:

if (type === 'video' && Platform.OS === 'android') {
      let dirs = ReactNativeBlobUtil.fs.dirs;
      const dirToSave =
        Platform.OS === 'ios' ? dirs.DocumentDir : dirs.DownloadDir;
      try {
        console.log('dirToSave', dirToSave);
        const destPath = dirToSave + '/' + filename + '.' + typeForTheFilename;

        debugger;
        await fs.copyFile(decodeURIComponent(el.uri), destPath);
      } catch (err) {
        console.log('err', err);
      }
      const res = await fs.readDir(dirToSave);
      debugger;
      console.log('res', res);
    }

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

I updated the code and I see that I do get true for the READ_EXTERNAL_STORAGE permission, so in theory I do have permission to read the file from there (or not?).

image

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

These are the Android permissions, I even added the "MANAGE_DOCUMENTS" to no avail:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

No you don't. Because the file does not belong to your app.
I would recommend reading a bit about scoped storage.
Basically by default you can only read / write within the scoped storage belonging to your app. And only your app, no other app and neither the user itself, has access to these files.
So if you want access to a file somewhere else (e.g. in the images/videos in the gallery) first of all the user has to select the file actively (you can't just query a random file, except with file manager permissions) , and then you need to use certain functions to be allowed to read these files.

They changed the whole storage system with Android 10, you still have the "old" system in mind.
But that's all the magic happening in java / kotlin you usually don't see when just programming your app in react-native.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

Thanks Ron, so the problem with the "content" file will still be there unless you find a way for all of us to be able to get it as an actual file inside the app's scoped storage. I wish react-native-image-picker had an option for us to save the actual file instead of providing a "content" link.

Meanwhile, I managed to write to the common "Downloads" folder using react-native-file-access, which is the opposite direction (I needed this in order to be able to save a downloaded PDF there, using their fetch). I wonder if this could be used somewhere in your library.

Thanks a lot!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

@afilp to the downloads folder you can save with my library on the new branch...
So there is no need for the additional library.
So basically you don't need the changes anyways? But asking about it in the first place?

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

Okay, thanks. Might help with my problem, that I have to use the cache file, but will see.

I wrote a new function copyToInternal which should do the work and copy the file to the apps internal storage. Just need to specify the contenturi and the targetpath.
But it is untested and I don't have time to test it right now. So feel free to test it :D Otherwise I might get to it tomorrow or on monday.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

Thanks a lot Ron, you are awesome!

The copyToInternal function can be used to also get the video file from the "content" uri in media store (in the format the picker gives) and upload it?

Or this is only for solving the initial problem with the "downloads"?

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

@afilp
Yes to the first.
This function will copy a file given by content URI (so the video you get from react-native-image-picker) to the apps internal storage.
And after that being done you can use it like every other file in the apps storage (so like the images you get from react-native-image-picker), and thus should be able to upload it.
So:

  1. pick the image
  2. copy it with await ReactNativeBlobUtil.MediaCollection.createMediafile(<contenturi>, <internal targetpath>)
  3. send it to the sever as usual

WIth being e.g. ReactNativeBlobUtil.fs.dirs.CacheDir

Saving to dowloads works too.
For that you need sth like the following:

    ReactNativeBlobUtil
            .config({
                fileCache: true
            })
            .fetch('GET', targeturi, {'Accept': 'application/octet-stream'}, JSON.stringify(dat))
            .then(async (res) => {
                let pathnew = await ReactNativeBlobUtil.MediaCollection.createMediafile({name: filename, parentFolder: '', mimeType: get_mimetype_for_file(filename)}, 'Download');
                await ReactNativeBlobUtil.MediaCollection.writeToMediafile(pathnew, res.path());
});

Explanation:
ReactNativeBlobUtil.MediaCollection.createMediafile(...) will create a new MediaStore file with the given name / subfolder / ...
ReactNativeBlobUtil.MediaCollection.writeToMediafile will write the data from the fileCache (so res.path()) into the new media storage file
So with these two function calls after the fetch resolved you're able to write the file to the public downloads folder.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

Thanks @RonRadtke

Meanwhile, I was reading a lot about this issue as many other users face it with pickers and found a post that said the he used stat and just took the actual file uri through this. I tried it and it did work (i.e. the actual video did upload):

        const stat = await ReactNativeBlobUtil.fs.stat(el.uri);
        el.uri = stat.path;

Was that just a lucky coincidence? Maybe because it was in Movies?

image

I wonder if this can be a solution (no need to copy anything) or it will fail in many other cases/devices/Android versions/etc,

Thanks.

from react-native-blob-util.

alariej avatar alariej commented on June 21, 2024

Very interesting discussion. Thanks @RonRadtke for working on this repo. I ended up here as I am also facing the issue of saving files to the shared "Download" folder.

With the old package, I was simply using RNFetchBlob.fs.dirs.DownloadDir (which returned /storage/emulated/0/Download) as the target folder, and then copying (with RNFetchBlob.fs.cp(sourcePath, targetPath)) files from the cache to the target.

In this package, ReactNativeBlobUtil.fs.dirs.DownloadDir returns /storage/emulated/0/Android/data/<myapp>/files/Download. After reading the comments here and in other forums, I was under the impression that this change was motivated by some change in the Android API, which would not allow to save to the shared Download folder anymore.

So I just quickly tested hardcoding the target to /storage/emulated/0/Download and tried copying files from the cache to the target. It worked both on a physical device running Android 9, and on an emulator (Pixel 5) running Android 11.

So I am not quite sure why ReactNativeBlobUtil.fs.dirs.DownloadDir doesn't return /storage/emulated/0/Download as before. Am I misunderstanding something?

Note: I just use the standard permissions READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE in the manifest.

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

This should not work unless you're using preserveLegacyExternalStorage=true.
It was changed because the way to aquire the paths was changed. The old methods ate flagged deprecated and the new return other values.
It came with one of the PRs I mergred after taking over the lib. But it is in general not possible to write there anymore, especially since the flag mentioned only works on targetapilevel < 30.
But as mentioned in one PR there would be the possibility to add a flag to get the old paths. Even though it won't help anyone targeting Android 11 (api 30), which is mandatory for all updates by now.

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

@afilp so copying to the app storage works. So basically all functionality you need is available on the feature branch.
I also managed to fix the issue with opening a file downloaded to the downloads folder, so we're getting closer for a release - at least if we skip the whole part in the config with overwrite / fileCache=false / ....

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

Excellent! I am looking forward to the release and use it asap!

Also, remember that I tried meanwhile to solve the problem of just copying to a "public" folder with another library?

See here, while it works in Android, I have a problem in iOS, it still tries to write in a "non-public" (app-specific) Downloads folder.
alpha0010/react-native-file-access#41

I am only mentioning it so that you may get inspired from this issue and maybe your branch solves this problem too. Thanks!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

@afilp as explained already. It's not possible.
The only option to offer would be an action for the user to save it manually by himself. But there the user probably would also see all other options about sharing the file.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

Sorry I have not realized this well, so the only way is to use the Share method of react-native and hope that uses will find the "Save" option.

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

Yes, at least if you want it saved there and are not fine with it being shared and thus be visible there.
I don't understand why you want to explicitly store it in downloads / documents and not just show it there. The user won't see the difference.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

@RonRadtke Maybe I get this wrong, but when react-native-blob-util downloads the PDF then we show an alert that says something like "PDF Downloaded". But then the user has no clue what to do next to get to the actual file. I wonder if there is a way to have a link to that particular folder in the FIles app, so that the Files app opens in that folder for the user to view the file immediately.

I thought that by being in a common Downloads folder that would be easier to find. At least in Android seems easier.

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

@afilp there are basically two options:

  • Tell the user he will find it in the documents folder (if you have sharing active and save it to the documents folder), a link to it no. But i mean if you store it directly to the documents folder it will be directly there without any subfolders I think
  • Open the file for the user with ReactNativeBlobUtil.ios.openDocument(destPath)

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

I did use both the previewDocument and the openDocument, like that:

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

However, it only works the first time during the entire app's session. It does not appear the next times I download a document, the same or others. Is this a bug I assume? Have you noticed this behavior? I do not know why this happens.

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

Yes that is for sure a bug.
If there is no issue yet, could you please create one for? So we don't have too much of a mess in this one here

from react-native-blob-util.

alariej avatar alariej commented on June 21, 2024

For info, I decided to go ahead for now with the cpExternal(sourcePath, fileName, dirType) function from https://github.com/alpha0010/react-native-file-access, which uses the MediaStore API. dirType can be 'downloads', 'images', 'audio' or 'video'.

It pretty much fits my app's use case since user files are already downloaded and in the cache, and just need to be copied and made available outside of the app. Would be great if react-native-blob-util eventually had a similar function.

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

Thanks @RonRadtke

Meanwhile, I was reading a lot about this issue as many other users face it with pickers and found a post that said the he used stat and just took the actual file uri through this. I tried it and it did work (i.e. the actual video did upload):

        const stat = await ReactNativeBlobUtil.fs.stat(el.uri);
        el.uri = stat.path;

Was that just a lucky coincidence? Maybe because it was in Movies?

image

I wonder if this can be a solution (no need to copy anything) or it will fail in many other cases/devices/Android versions/etc,

Thanks.

@RonRadtke I just tried it in a tablet, picking (with image-picker) an mp4 video from the Downloads folder which I downloaded previously from Google.

I get this error, is there any way to solve this? Because it will not get the actual file to download as video and we are again at ground zero with our app :-) :

image

Note: I even used decodeURIComponent(el.uri)

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

The strange thing is that the behavior is different (it works) in my Android phone, while both have Android 11 version.

I assume they have a different way to store the "Downloaded from Google" video file.

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

@afilp it's released!

The issue with stat is what I described earlier. There is no guarantee for the row actually pointing to a file. And the issue you're seeing here is exactly that. There is no way to solve that except for switching to the newly introduced APIS

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

I'll reopen the issue again - just in case you have feedback.
If there are any new bugs - please create a new issue though

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

Thanks Ron!

Maybe you missed to type the related code here?

image

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

@afilp it's released!

The issue with stat is what I described earlier. There is no guarantee for the row actually pointing to a file. And the issue you're seeing here is exactly that. There is no way to solve that except for switching to the newly introduced APIS

With your new v0.14 we do not need to do anything else other than use it, correct? Not sure what you meant with except for switching to the newly introduced APIS. This is done on v0.14, correct? Or we have to manually do something more? Thanks!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

Thanks Ron!

Maybe you missed to type the related code here?

image

no, that's exactly want I meant to write. You get a database row (or multiple) from a query. But probably it would be better to write cursor instead of row, since we get a cursor not a single row.

@afilp it's released!
The issue with stat is what I described earlier. There is no guarantee for the row actually pointing to a file. And the issue you're seeing here is exactly that. There is no way to solve that except for switching to the newly introduced APIS

With your new v0.14 we do not need to do anything else other than use it, correct? Not sure what you meant with except for switching to the newly introduced APIS. This is done on v0.14, correct? Or we have to manually do something more? Thanks!

You need to use the new functionality. it's not a replacement for the old one, it's an addition. So you will have to call the correct functions as described in the wiki.

from react-native-blob-util.

jaedontym avatar jaedontym commented on June 21, 2024

Hi @RonRadtke, thank you for working so actively on this library. It's been really useful for developers like me who aren't familiar with native Android code. You have no idea how happy I was to see v0.14 released this morning!

On that note, I've been trying to get v0.14 to work on Android 7 but I'm having issues. I'm thinking one of the issues might be in this method from ReactNativeBlobUtilMediaCollection.java (added my questions as comments in the code):

public static Uri createNewMediaFile(FileDescription file, MediaType mt) {
    ...

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
     ...
    } else {
        File directory = Environment.getExternalStoragePublicDirectory(relativePath);
        if (directory.canWrite()) {
            File f = new File(relativePath + '/' + file.getFullPath()); // <-- Instead of relativePath, should it be directory.getPath() instead? Right now this seems to be causing the createNewMediaFile method to return null
            if (!f.exists()) {
                boolean result = f.mkdirs(); // <-- Should this be f.createNewFile() instead? Right now it's creating a folder
                if (result) return Uri.fromFile(f);
            }

        }
    }

    return null;
}

I tried replacing making the following replacements and the file got created successfully in the public downloads folder:

  • directory.getPath() instead of relativePath
  • f.createNewFile() instead of f.mkdirs()

However this led to another problem in the writeToMediaFile() method:

Screenshot 2022-02-07 at 11 22 23 AM

Here's a relevant snippet of my React Native code:

  ReactNativeBlobUtil
     .config({
       fileCache: true,
       path: `${Platform.OS === 'ios'
         ? ReactNativeBlobUtil.fs.dirs.DocumentDir
         : ReactNativeBlobUtil.fs.dirs.DownloadDir
       }/${filename}`,
     })
     .fetch('GET', url, {...})
     .then(async (res) => {
       if (Platform.OS === 'ios') {
         ...
       } else {
         const result = await ReactNativeBlobUtil.MediaCollection.copyToMediaStore(
           {
             name: 'TestFile',
             parentFolder: '',
             mimeType: 'application/pdf',
           },
           'Download',
           res.path(),
         );
       }
     });

With this implementation, the file gets saved properly to the app's scoped "Download" directory but doesn't get copied to the shared storage "Download" directory.

I'm not sure if this is considered a new bug. If it is, I'll create a new issue as requested.

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

Hi @jaedontym,
thank you very much for appreciating my work here. It really helps to now people do like and sue my work.
And thanks for the bug report.
You're partly right.
I absolutely forgot to actually create the file. I'm currently only creating the parent directories, but we have to create these too, we can't just only create the file. This will otherwise lead to an error if the parent directory doesn't exist.
I am working on a fix for it, I just want to test it first since I did some more clean up and adding that the write method works for older APIs too.

from react-native-blob-util.

jaedontym avatar jaedontym commented on June 21, 2024

@RonRadtke Thank you so much. Can't wait to integrate the updates to get my app working properly with the newer Android versions!
I'll continue reporting relevant bugs here as I find them :)

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

@RonRadtke Just to make sure, the pickers store the files in the "Cache", right? So this gets bigger and bigger? Or is it automatically cleaned when closing the app, etc.?

image

I am asking in case I have to manually delete the selected images/videos after the user uploads them to the server, using react-native-blob-util.

If yes, should I use this method of yours? ReactNativeBlobUtil.fs.unlink or ReactNativeBlobUtil.fs.mv ?

Thanks!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

Yes that will grow. Unlink would be the way to go.

from react-native-blob-util.

HagarAyad avatar HagarAyad commented on June 21, 2024

I'm facing problem with copyToMediaStore , I get this error : Failed to get output stream
here is my code
const result =
await ReactNativeBlobUtil.MediaCollection.copyToMediaStore(
{
name: eventTitle,
parentFolder: '',
mimeType: 'application/pdf', // MIME type of the file
},
'Download',
${filePath},
);
and filePath is : /storage/emulated/0/Android/data/com.runofshow/files/Download/Test.pdf

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

Yes that will grow. Unlink would be the way to go.

I tried this, but when I reload the app I still see the photo there in the UI (it should have been a "missing image").

        formData.forEach(el => {
          if (el.origPath) {
            ReactNativeBlobUtil.fs.unlink(el.origPath);
          }
        });

This is the code for the origPath:

      formData.push({
        name,
        filename,
        type: el.type,
        origPath: el.uri,
        // Change BASE64 encoded data to a file path with prefix `ReactNativeBlobUtil-file://`.
        // Or simply wrap the file path with ReactNativeBlobUtil.wrap().
        data: ReactNativeBlobUtil.wrap(
          decodeURIComponent(
            Platform.OS === 'ios' ? el.uri.replace('file://', '') : el.uri,
          ),
        ),
      });

Is unlink working? (it does not seem so)

UPDATE: I do see the photo in the UI, but when trying to re-upload it it shows as missing. So why the UI shows it while it is not there?

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

What type is the response data, is it always a boolean? (could not find that in docs)
I am using JSON.parse as I though it can also be an object and I need to return it back to react-query:

image

So, what should we expect for "data" there? Thanks! (on uploading success I mean)

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

UPDATE: I do see the photo in the UI, but when trying to re-upload it it shows as missing. So why the UI shows it while it is not there?

Which UI?

So, what should we expect for "data" there? Thanks! (on uploading success I mean)

Whatever your server sends. The data is the raw value your server is sending, or, if your config is specifying to store it in a file, the path.
So depending on what your goal is, it might be better to use the text() or json() since this will resolve a file path and return the data sent by the server.

from react-native-blob-util.

jaedontym avatar jaedontym commented on June 21, 2024

@RonRadtke Hi Ron! How are the fixes coming along? I hope that no additional complications arose 😰
I went ahead with the app internal storage implementation first but this is affecting the UX of downloading and uploading files quite a bit (most users don't know how to navigate to the directory). Fortunately that's not the app's main feature but I do hope to be able to get this sorted out in time.

As always, really appreciate your work to keep rn-fetch-blob alive and up-to-date even with your busy schedule!

from react-native-blob-util.

RonRadtke avatar RonRadtke commented on June 21, 2024

@jaedontym Update is released

from react-native-blob-util.

afilp avatar afilp commented on June 21, 2024

@RonRadtke Yes, all seems well in Android now! I did not even need that fix, most likely because our users have recent Android versions, but of course it is nice to have this too!

My other (unrelated) problem is when users pick in iOS many heavy images through the picker (8MB each for example) and I show them in a Flatlist. When there are 15+ of them, I have reports that the app is crashing when scrolling to the end of the list.

If you happen to know how to cope with this issue I will greatly appreciate it! I am thinking of using react-native-resizer, create duplicate smaller images as thumbnails, to show these in Flatlist, while using react-native-blob-util to upload the original large images one-by-one. But all this is a hassle, Android does not crash, not sure if there is another way in iOS.

from react-native-blob-util.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.