Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does not get absolute paths as claimed. Cannot write to a .txt file #40

Closed
bradyt opened this issue Feb 23, 2019 · 39 comments
Closed

Does not get absolute paths as claimed. Cannot write to a .txt file #40

bradyt opened this issue Feb 23, 2019 · 39 comments

Comments

@bradyt
Copy link

bradyt commented Feb 23, 2019

I don't understand what this can and can't do.

Can it be used to write to files? I'm getting errors? You say it returns absolute file paths, but that does not look to be true. It seems to copy the filename only, not the path, and put it in private external storage, especially, cache.

So this is not really a file picker. It only copies files temporarily to cache?

@bradyt bradyt changed the title Make features more clear Does not get absolute paths as claimed. Cannot write to a .txt file Feb 23, 2019
@bradyt
Copy link
Author

bradyt commented Feb 23, 2019

Whether I select from Documents folder or Google Drive, it's returning /data/user/0/info.tangential.cone/cache/.cone.ledger.txt, which doesn't seem like the absolute path at all.

I/FilePicker(12301): URI:content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3D8
E/FilePathPicker(12301): +++ API 19 URI :: content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3D8
E/FilePathPicker(12301): +++ Document URI
I/FilePicker(12301): Cloud file loaded and cached on:/data/user/0/info.tangential.cone/cache/.cone.ledger.txt
I/FilePicker(12301): Absolute file path:/data/user/0/info.tangential.cone/cache/.cone.ledger.txt
I/flutter (12301): /data/user/0/info.tangential.cone/cache/.cone.ledger.txt
I/flutter (12301): File: '/data/user/0/info.tangential.cone/cache/.cone.ledger.txt'
W/System  (12301): A resource failed to call close. 

@miguelpruivo
Copy link
Owner

miguelpruivo commented Feb 23, 2019

This is not an issue. It’s a feature actually and that’s the normal behavior. You are trying to load the absolute file path from a cloud file. Cloud files don’t have any actual paths until a copy is cached into the device.

If you need to open a cloud file, the picker will load it from you (thus caching) and then you are free to use it and save it if modified.

Also you can’t delete files from cloud with file picker.

@bradyt
Copy link
Author

bradyt commented Feb 23, 2019

The same occurs for Documents folder.

@korovan-software
Copy link

So why we need WRITE_EXTERNAL_STORAGE permission?
I agree that the feature to get direct path is more impotant that feature to get a copy.
In Android you can return content provider path and user will able even to write into Google Drive and so on.

@miguelpruivo
Copy link
Owner

miguelpruivo commented Mar 16, 2019

@Footmau Because cloud files need caching before having a path. If you want to actually access a file within Flutter, you need to have its data saved somewhere and not virtualized. Let’s say you want to modify it, how would you modify a file that is in the cloud?

This package is all about let the user access files, not actually pick paths to write despite though that being possible anyway.

@korovan-software
Copy link

@miguelpruivo Of course, thanks for that features.
But you can improve it:

Makes it possible for your app to have long term, persistent access to documents owned by a document provider. Through this access users can add, edit, save, and delete files on the provider.

@miguelpruivo
Copy link
Owner

miguelpruivo commented Mar 16, 2019

@Footmau again, that’s hard to handle. That would make sense for an Android only plugin. iOS behaviors completely different.

I have trying to keep a seamlessly feature experience between platforms as much as possible, so less experienced users don’t get confused about why one side supports and other don’t, having to create themselves, in app workarounds.

This is also happening for getting directory paths in which android supports it quite easily from API 21 and above but iOS doesn’t have that concept. That’s one of the reasons the #23 is still open to discussion.

@Stephen-Cronin
Copy link

I know this is closed, but will add my situation in here too.

I'm trying to select a file from the device and store the path so that I can reopen the file again next time the app loads. The file is likely to have changed in the meantime, so loading it from the cache location is no good. I either need the original file path or the user needs to use the picker every time (which isn't a great experience).

I'm not sure why I am getting a cached version of the file. Maybe it is because I'm trying to access a file in the Documents folder (in Android) that was created by another app (maybe I can't access it directly anyway)? The files in the Download folder aren't cached.

Anyway, if I'm trying to do something that's not possible, sorry for wasting your time. But if local files are sometimes cached for other reason, then it would be great if there was a way to access the path of the original file as well. Thanks.

@bradyt
Copy link
Author

bradyt commented Jun 27, 2019

If I pick a Dropbox file with this plugin, and write to it, should I see the changes on Dropbox?

@miguelpruivo
Copy link
Owner

@bradyt no. You don’t have write permissions with it, neither you can have a URI that points to a remote path. Remote files will be loaded and cached for you to use.

Afterwards, you can export it and let the user decide where to save to.

@bradyt
Copy link
Author

bradyt commented Jun 27, 2019

Thank you for clarifying.

Do you think I should be able to set up some sort of seamless experience for Android users, by following your project as a tutorial (tutorial in Java ecosystem, Platform Channels, etc), and using concepts from https://developer.android.com/guide/topics/providers/document-provider? I saw the following statement there.

Through this access users can add, edit, save, and delete files on the provider.

I am under the impression you used SAF in your file_picker project.

I am hoping that my app can read and write to files or a directory without the user having to do anything after the initial selection of files or directories. That is, no need to export or upload.

Thank you for looking at my situation.

@miguelpruivo
Copy link
Owner

miguelpruivo commented Jun 27, 2019

@bradyt thank you for your suggestion and I've already thought of that, but that means that it would act more like a file manager instead.

The purpose of this plugin is to act just as a file picker providing a seamless experience between iOS and Android. With that said, you don't have a similar concept in iOS and that would mean that devs would have to handle the flow in a different way for both platforms. This was already pointed in this answer.

As it is currently, you know that it will behave the same way for both iOS and Android, and IMO that's a plus.

@miguelpruivo
Copy link
Owner

@Stephen-Cronin sorry for being late in response, probably I've might missed it. If your files are being cached, that's probably because they're being loaded from a remote URI (eg. GDrive, Dropbox, etc.) and you can't get an absolute path for those files, that's why they're all cached.

However, IMO, you can handle that yourself by creating an app's content directory and when the user loads/picks a file and it is from a cached directory, save it in your app's content folder.

@bradyt
Copy link
Author

bradyt commented Jun 27, 2019

Sorry I knew I wasn't going to make sense there. What I meant to say was, am I close to accomplishing this if I fork the project?

@miguelpruivo
Copy link
Owner

@bradyt I'd say yes, but you'll need to work out the Android side implementation side. Also, currently it is supporting all API (from 16 to 28) which makes it also very flexible. Using document provider only, would limit access to API 19 and above.

@bradyt
Copy link
Author

bradyt commented Jun 27, 2019

Oh, I assumed you were using this. Thank you for the help and pointing out the caveats. I think making progress on storage options for my app will outweigh that drawback. But maybe I'll explore a more complete solution in the future.

@Stephen-Cronin
Copy link

Stephen-Cronin commented Jun 30, 2019

@miguelpruivo Thanks for the response. In my case, I am trying to load a file created by a third party game and stored in the phone's Documents folder (on Android). This is the path to the file on my phone:

/storage/emulated/0/Documents/Sports Interactive/Football Manager 2019 Mobile/normal/games/fm_save4.dat

It's not a remote URI, it's on the phone, but file_picker is caching it to:

/data/user/0/com.example.fmm_app/cache/fm_save4.dat

I want to be able to store the direct path, so when the user plays the game and the file changes, they can reload it through my app without needing to go through file_picker again (as it's relatively slower and more clicks etc). The cached version is out of date as soon as they play the game again, so is useless.

If I hard code in the direct path, I can open file, so it seems I have permissions to access it even though it's in a Documents folder belonging to another app. Is there anyway file_picker can provide this path? Is there some sort of bug preventing it? Or more likely, something I'm missing? Thanks!

@miguelpruivo
Copy link
Owner

miguelpruivo commented Jun 30, 2019

@Stephen-Cronin this is happening probably because it’s being stored within other apps private content folder (in this particular scenario, the game) so it needs to be cached (copied) to protect direct access.

Nevertheless, might to post the full log when you pick the path so I can check the full trace as well as your flutter doctor -v? Also, make sure you’re running the latest file_picker version (1.3.7).

Thank you.

@Stephen-Cronin
Copy link

@miguelpruivo Thank you!

I was on file_picker version 1.3.4+1, not 1.3.7. I updated pubspec.yaml to say 1.3.7 and ran both flutter pub upgrade and flutter packages upgrade (because I'm paranoid), so I assume I'm on 1.3.7 now - though I can't see an easy way to verify that. Anyway, still having the issue.

I've attached the log from debug console and the flutter doctor -v output:

flutter-debug-log.txt
flutter-doctor-v.txt

It is saying "Caching file from remote/external URI", but above that you can see that the file URI it gets is:

File URI:content://com.pleco.chinesesystem.localstorage.documents/document/%2Fstorage%2Femulated%2F0%2FDocuments%2FSports%20Interactive%2FFootball%20Manager%202019%20Mobile%2Fnormal%2Fgames%2Ffm_save4.dat

I'm not sure why it has the Pleco URL/namespace in there. I do have the Pleco app installed, but it's not showing up in the Android file picker at all (ie I'm not navigating anywhere near it) and it should have nothing to do with this. Maybe that's the key?

As I mentioned before, I can open the file by providing the direct path to it (in the game's folder) and I've now verified that _file.path returns the direct path, so it seems my app has direct access to that folder, just not file_picker. Maybe it's the Pleco stuff interfering or maybe it works only because I actually know the path in advance (ie it won't return the file if you pick it rather than already knowing)?

Thanks again for your help.

@miguelpruivo
Copy link
Owner

miguelpruivo commented Jul 1, 2019

@Stephen-Cronin would you mind to run in on a difference device or emulator and see if it's still happening? It looks like you have a third party file explorer installed and its acting as document provider, thus, the URI is handled like an external file (so that explains the caching process).

It's not quite easy on Android to cover all the API/ROM when they use third party explorers, that's why I always suggest to stick with the native one (when supported - usually after API 19 in native roms).

@Stephen-Cronin
Copy link

@miguelpruivo I'd have to set up the emulator (install the game, wait for the data to download etc), so I removed FX file explorer (no change) and Plece (change) from my phone. I am on a Samsung Galaxy S7 so it has the Samsung file explorer rather than the default Android one, but not much I can do about that easily (or can expect my users to do).

Anyway, now the file URI line is as follows:

[SingleFilePick] File URI:content://com.android.externalstorage.documents/document/home%3ASports%20Interactive%2FFootball%20Manager%202019%20Mobile%2Fnormal%2Fgames%2Ffm_save4.dat

So it's gotten rid of the Pleco namespace/URI but is still treating it as external storage. I still have permission to open the file directly from my app if I hardcode the URI in and also to read the file path from the file, but maybe you can't get that through the file picker. I'll try on the emulator tomorrow to see if that's the same. Thanks again for your help.

@Stephen-Cronin
Copy link

@miguelpruivo I tried with an emulator using a stock standard Nexus 5 and got the same thing:

[SingleFilePick] File URI:content://com.android.externalstorage.documents/document/home%3ASports%20Interactive%2FFootball%20Manager%202019%20Mobile%2Fnormal%2Fgames%2Ffm_save4.dat

So it looks like it is indeed because the Documents folder is treated as external (unless the one for your app). I guess I'm out of luck. Thanks anyway for your help, it's much appreciated.

@miguelpruivo
Copy link
Owner

@Stephen-Cronin when you use the content://com.pleco.chinesesystem.localstorage.documents/document/%2Fstorage%2Femulated%2F0%2FDocuments%2FSports%20Interactive%2FFootball%20Manager%202019%20Mobile%2Fnormal%2Fgames%2Ffm_save4.dat directly, does that work for you?

It would be great if you could check it directly with that URI, so I can further debug this. Also, can you give me with the steps to replicate this?

Thank you.

@bradyt
Copy link
Author

bradyt commented Jul 29, 2019

I've started a plugin that allows persistent reading writing to a .txt file from local or cloud storage, at https://github.com/bradyt/uri_picker/. Currently it is licensed for public domain, I hope that allows other plugins such as this one to add similar features.

@miguelpruivo
Copy link
Owner

@bradyt thank you. You should be able to write/read any file with file_picker, however to write, typically you use the File(path) handler from there. But I can understand that for cloud files, this isn't currently supported and I strongly would like to add to it some of the requested features in #99, problem is I haven't had much time lately.

@bradyt
Copy link
Author

bradyt commented Jul 30, 2019

@bradyt thank you. You should be able to write/read any file with file_picker, however to write, typically you use the File(path) handler from there. But I can understand that for cloud files, this isn't currently supported and I strongly would like to add to it some of the requested features in #99, problem is I haven't had much time lately.

I'm confused. So your stance is that when I write to the File(path), that for example the contents of a file in the Documents/ folder will update? I thought the file you're returning is only cached or in app memory. Not writing to the file shared at Documents/ folder.

@miguelpruivo
Copy link
Owner

@bradyt if you're picking a file from Documents folder or any other folder, and write directly into to exact same location, it should stay there. However, for some files, such as remote files (GDrive), it will be cached files, and in this particular scenario, you can't write directly in the same folder.

I've checked your implementation and you can allow it because you're writing it directly on the native side (Android) by providing the URI. That's a different concept from what file_picker does, and may be suitable for some cases but not for others (like handling a file purely in Dart, for example).

Nevertheless, when I get some time, I should further investigate a possibility to cover the good of both ends for a near future.

@bradyt
Copy link
Author

bradyt commented Jul 30, 2019

@bradyt if you're picking a file from Documents folder or any other folder, and write directly into to exact same location, it should stay there.

I am confused. Your description there seems to conflict with the following.

Your library is using ACTION_PICK and ACTION_GET_CONTENT.

Reading from https://developer.android.com/guide/topics/providers/document-provider.

  • Use ACTION_GET_CONTENT if you want your app to simply read/import data. With this approach, the app imports a copy of the data, such as an image file.
  • Use ACTION_OPEN_DOCUMENT if you want your app to have long term, persistent access to documents owned by a document provider. An example would be a photo-editing app that lets users edit images stored in a document provider.

@bradyt
Copy link
Author

bradyt commented Jul 30, 2019

Ah, maybe you mean that you can get the content with your library, then use a different means to write back to the original location, like some separate library that does share or export.

@miguelpruivo
Copy link
Owner

miguelpruivo commented Jul 30, 2019

@bradyt sure, the problem with the document provider, is that is only available after API 19, which somehow will remove the 100% coverage that the plugin currently has (starting with API 16). So that's the reason the ACTION_GET_CONTENT is being used instead and the path is then extracted from it and returned to the user, instead of making a copy of the file (unless, like mentioned, we are talking about remote files, which are impossible to retrieve a path to a local file, without caching it first).

@bradyt
Copy link
Author

bradyt commented Jul 30, 2019

Yes, I agree, the version limitation, as well as getting things to work across platforms, especially to iOS, will take some thought. I hope that my efforts will help one of any of our packages in the ecosystem to take on more functionality.

I wanted to get the easiest functionality available for a reasonable subset of potential users, so I jumped at ACTION_OPEN_DOCUMENT, as it made my task easier. At some point I would like to combine any libraries to get functionality to more devices.

@bradyt
Copy link
Author

bradyt commented Jul 30, 2019

I'm very confused by your use of cloud vs local. It really seems to conflict with what I've been trying to clarify.

@miguelpruivo
Copy link
Owner

Yes, that's another thing that I value and want to keep as long as possible, a seamless experience across each platform, without having some features that works in one but not the other that would make the dev to be aware of such limitations and implement workarounds himself.

If it wasn't because of this, some features like picking only a directory (which Android supports but I believe to be at this point impossible on iOS without picking a file) would be already implemented.

@bradyt
Copy link
Author

bradyt commented Jul 30, 2019

@miguelpruivo Can you tell me precisely which additional functionality your app provides in local files vs cloud files?

I'm not seeing the difference in how they're treated.

You seem to be suggesting that your library can write to local files, but not to cloud files.

But that seems to conflict with what I quoted above. That it's only reading in a cached copy, at best. No way to write back to the original location with the same path your library returns.

@miguelpruivo
Copy link
Owner

@bradyt your assumption is correct, tl;dr:

I let the user pick a file locally, and then extract the absolute path from it so it can be manipulated with Dart directly, whenever it's possible.

When you pick a file from a Dropbox, GDrive, etc. you must cache it in order to have a local file to work with. It can be confusing because you may expect to edit directly that file as you have a path, I agree with you. In the end you're just editing a cached copy that you should take care and save somewhere else (if your app should have this feature at all, because you may want only to display, share, etc.).

@bradyt
Copy link
Author

bradyt commented Jul 30, 2019

Okay, I'll have to try it again soon. Thank you for clarifying.

@deacon78
Copy link

Same issue with "Downloads" directory on my Android 8.0 smartphone.
I got a file named "TEMPLATE_ALFA ROMEO_GT_2020-07-23.zip.tpl" in this Download directory.
I pick this file in my app using
filePath = await FilePicker.getFilePath(type: FileType.any);
If I print "filePath" I get filePath= /cache/file_picker/
So I am not able to get the real absolute file Path...

I do not reproduce on Android Emulator with an Android 8.0 smartphone Emulated...
On AVG I got the real absolute filepath : /storage/emulated/0/Download/TEMPLATE_ALFA ROMEO_GT_2020-07-23.zip.tpl

@deacon78
Copy link

deacon78 commented Jul 23, 2020

I obtain the good absolute file path if I do not pick the file from the "download" shortcut, but directly in the smartphone's download directory which is /storage/emulated/0/Download/
Weird...it's like file_picker cannot read the file when it pass through the "Downloads app".

@jerinho
Copy link

jerinho commented Jun 25, 2022

This is not an issue. It’s a feature actually and that’s the normal behavior. You are trying to load the absolute file path from a cloud file. Cloud files don’t have any actual paths until a copy is cached into the device.

If you need to open a cloud file, the picker will load it from you (thus caching) and then you are free to use it and save it if modified.

Also you can’t delete files from cloud with file picker.

it's not just cloud file. it's simple. please understand properly. your file_picker which supposed to be easily provide real path doesn't do that. it's all about file information not involving file accessing. 3 years and it still doesn't

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants