Search Unity

Simple File Browser [Open Source]

Discussion in 'Assets and Asset Store' started by yasirkula, Nov 19, 2016.

  1. blevok

    blevok

    Joined:
    Feb 16, 2017
    Posts:
    75
    Hi, i'm having an issue with opening files on android. It was reported to me by some of my users, and i've been able to reproduce it on android 12, but i assume it's happening on android 11+.

    When i open the file browser, i'm able to navigate through the drive, and pick out a file and open it, and everything works normally, just like it did in android 9.
    But instead of navigating to a file and opening it, if i click the button at the bottom of the left pane and choose a folder to add to the left pane, and then open a file using that method, it doesn't work and throws this error in ADB. "UriFormatException: Invalid URI: The hostname could not be parsed."

    The issue does not happen when i open videos, which i pass to AVPro, or when i open bmp images, which i pass to a custom bmp loader, or when i open png/jpg images the normal way, which i pass to UnityWebRequestTexture. It only happens when i open png/jpg images via the folder select method. So the same image works one way but not the other.

    Also i've found that if i use the left pane method to add a folder, the normal way of navigating through folders to select a file doesn't work anymore. If i clear the app data, then it works again, but only until i add a folder again.

    I've just updated to the latest version and tested it on an android 12 device, and there is no change.

    So i have 2 questions.
    1. What's the difference between the two ways of selecting a file, and do you have any thoughts about why one way isn't working when used with UnityWebRequestTexture? I'll include my code below.
    2. Is there any way to preserve or restore the normal file selection method after using the folder adding method?

    Code (CSharp):
    1. string currentImageViewerImagePath = FileBrowser.Result[0];
    2. byte[] currentImageViewerImage = FileBrowserHelpers.ReadBytesFromFile(currentImageViewerImagePath);
    3. Texture2D CurrentImage = new Texture2D(2, 2);
    4. using (UnityWebRequest www = UnityWebRequestTexture.GetTexture("file://" + currentImageViewerImagePath))
    5. {
    6.     yield return www.SendWebRequest();
    7.     if (www.isNetworkError)
    8.     {
    9.         Debug.Log(www.error);
    10.     }
    11.     else
    12.     {
    13.         CurrentImage = DownloadHandlerTexture.GetContent(www);
    14.     }
    15. }
     
  2. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Hi!

    1. Quoting the documentation:
    So you can't pass them as filepaths on Android 10+. You can load the Texture via
    CurrentImage.LoadImage(currentImageViewerImage);
    instead. The reason AVPro is working might be that it supports SAF paths internally. If you wish, you can also try passing SAF path to UnityWebRequestTexture without file:// prefix and see what happens.

    2. I couldn't fully understand this issue. Could this be related to my answer to (1)?
     
  3. blevok

    blevok

    Joined:
    Feb 16, 2017
    Posts:
    75
    Well that's one of the weird parts about this, it does work with normal file paths. I know this for 2 reasons.
    1. AVPro isn't doing any SAF stuff. Maybe version 2 does, but i'm using version 1, which i haven't updated since android 9, because they removed features in the new version that are vital to my app.
    2. It doesn't just work for videos, loading images with UnityWebRequestTexture does work with the file:// path. If i don't use the SAF method, it works the same on android 9 and 12. Same thing with loading subtitle files and audio files, they work with normal file paths.
    I understand that this isn't supposed to work, but it does. It was one of my users that informed me about this workaround, and i initially thought he was crazy, but i tested it on an android 12 phone and found that it works. And it also works with the android studio api 31 emulator that's not in developer mode.

    I'm using UnityWebRequestTexture because it's the only way i've found that doesn't freeze the UI while it's processing. It's a VR app, so i can't use LoadImage since having head tracking stop working every time an image is loaded would be more destructive to the app's rating than the image viewer just not working without using the workaround.

    But moving on, you were right about the file:// prefix being a problem. I used string.contains to make a conditional function to check for content:// and use that to decide whether or not to add the file:// prefix.
    That keeps the file path method working, and it gets the SAF method a little closer to working. Now i'm getting this error: ""Error Unity Curl error 1: Protocol "content" not supported or disabled in libcurl".

    I've done some searching about using UnityWebRequestTexture with android SAF paths, but so far none of the solutions i've found have worked.
     
  4. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    I'd recommend calling File.Exists on the returned filepath and if it returns false, copy the selected file to temporaryCachePath via FileBrowserHelpers.CopyFile and load the image from there. You can copy the file in a separate thread but you'll need to modify FileBrowserHelpers and add an async variant to CopyFile that uses AndroidJNI's thread functions as demonstrated here: https://github.com/yasirkula/UnityN...gins/NativeGallery/NativeGallery.cs#L710-L736
     
  5. twisterella

    twisterella

    Joined:
    Mar 20, 2022
    Posts:
    1
    I'm trying to get this to work on an Mac app that is available through the Mac app store but not having much luck. Any suggestions. It works fine if not sandboxed as mac app store app
     
  6. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
  7. icaroleles1

    icaroleles1

    Joined:
    Feb 18, 2018
    Posts:
    6
    where are those settings?
     
  8. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
  9. icaroleles1

    icaroleles1

    Joined:
    Feb 18, 2018
    Posts:
    6
    yasirkula likes this.
  10. blevok

    blevok

    Joined:
    Feb 16, 2017
    Posts:
    75
    Sorry i disappeared, work got crazy busy for a while, so i had to put this on hold until things calmed down.

    Anyway, i've confirmed that there's nothing wrong with the paths from the file browser. File.Exists returns true, copying the file works without issue, and i can pass those paths to most unity functions just fine. It's actually UnityWebRequestTexture that isn't handling the SAF paths properly. So i just have to bug unity about it or create my own way of processing a texture on a separate thread.

    Thanks again.
     
    yasirkula likes this.
  11. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    If System.IO.File.Exists returns true for a path, it can't be a SAF path to my knowledge. Only raw filepaths work with File.Exists. In any case, I don't think Unity will provide a solution for this so I'd still recommend copying the file to temporaryCachePath in a thread (or on main thread) running UnityWebRequestTexture from there.
     
  12. blevok

    blevok

    Joined:
    Feb 16, 2017
    Posts:
    75
    Oh i meant FileBrowserHelpers.FileExists returns true. I don't use System.IO for anything storage related anymore. And yes i agree that Unity probably won't do anything to improve UnityWebRequestTexture, so i'll probably just do what you suggested. Thanks.
     
    yasirkula likes this.
  13. Yann_de_Couessin

    Yann_de_Couessin

    Joined:
    Jan 2, 2015
    Posts:
    40
    Hello fellas !

    First, many thanks for your support on this open source plugin.

    I encountered an empty file browser upgrading my project from Unity 2021.2 to 2021.3
    It still lists folders, but no files can showup. I have no errors of flags in the console.

    Someone had this behaviour too ?

    Regards !
     
  14. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
  15. Yann_de_Couessin

    Yann_de_Couessin

    Joined:
    Jan 2, 2015
    Posts:
    40
    Oh thanks a lot @yasirkula !
    I tried to search for these terms but missed the "closed" issues in the Github. Silly me :)
     
    yasirkula likes this.
  16. David69

    David69

    Joined:
    Oct 2, 2018
    Posts:
    31
    Doesn't work with build exe - as it uses EditorUtility.OpenFilePanel !!
     
  17. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    SimpleFileBrowser doesn't use that function. You must be confusing with another plugin.
     
  18. santa992

    santa992

    Joined:
    Mar 3, 2017
    Posts:
    10
    Yasirkula, you are great guy and thank you on all this assets you put on store. I have 1 question, reading files work great on windows and android, and on IOS guy who are testing see only I think local app files I am not sure about this but IOS use some icloud. What I need is to get xml data from phone, and I use it for select folder that contains audio, then when I need I copy mp3 to local app folder and play that work great, but can this work on IOS, I see you have some native picker asset do I need to switch to that asset?
     
  19. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    If I understand you correctly, you're picking an xml file with SimpleFileBrowser. That xml file contains a directory's file path. From that file path, you're fetching audio files without using SimpleFileBrowser. This plugin has no access to iCloud, you're right. For that, you can use NativeFilePicker as you've suggested. NativeFilePicker will always show the file selection dialog though, so you can't read a directory path from the xml that's picked from iCloud and download the audio files from the stored directory without showing a user interface to the user, that's not possible.
     
  20. santa992

    santa992

    Joined:
    Mar 3, 2017
    Posts:
    10
    You are the best! You have beer from me! No I use this in 2 scenarios. 1st get xml ( actually read txt then parse with xml parser or however), and 2nd get mp3 files from folder ( i am just copy mp3 to app folder so I can use it with webrequestmultymedia). But biggest problem is to access folders on IOS, and I need to copy him like I describe.
     
    yasirkula likes this.
  21. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Thank you for your support! It's currently not possible to access iCloud via this plugin and you can't pick a folder with NativeFilePicker unfortunately. If you're picking a single mp3 file, then NativeFilePicker.PickFile should do wonders on iOS. For multiple mp3 files, your best bet (among my plugins) would be to use NativeFilePicker.PickFiles.
     
  22. valkirin2000

    valkirin2000

    Joined:
    Sep 27, 2022
    Posts:
    4
    Hello, @yasirkula !
    Thanks a lot for the great asset!
    I use your example code for import mp3 file to my game. It works properly on Windows.
    Now I'm trying to import mp3 on Android 13, but I don't get a path.
    Could you tell me what I do wrong?

    Here log from Logcat Unity.
    LogCat.jpg
     
  23. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    @valkirin2000 Hi! The path is correct. That's a Storage Access Framework path (which is an abstraction layer). So the path won't work with System.IO functions. You'll need to copy it to temporaryCachePath via FileBrowserHelpers.CopyFile in order to use it. You can see how that can be done at the end of the example code.
     
  24. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    BTW I'd recommend using NativeFilePicker on Android & iOS platforms since it uses device's native file picker user interface.
     
  25. valkirin2000

    valkirin2000

    Joined:
    Sep 27, 2022
    Posts:
    4
    I did it this way:

    destinationPath = Path.Combine(Application.persistentDataPath, FileBrowserHelpers.GetFilename(FileBrowser.Result[0]));
    FileBrowserHelpers.CopyFile(FileBrowser.Result[0], destinationPath);
    StartCoroutine(LoadAudioClip());
    }
    }
    IEnumerator LoadAudioClip()
    {
    using (UnityWebRequest uwr = UnityWebRequestMultimedia.GetAudioClip(destinationPath, AudioType.MPEG))
    {
    yield return uwr.SendWebRequest();
    AudioKeeper.Clip = DownloadHandlerAudioClip.GetContent(uwr);
    }
    }
    }

    I just added IEnumerator LoadAudioClip() in your example code. As I told you it Works with Windows. I'm making a game where user can pick music from phone and app generates level according audio track.
     
  26. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Can you try prepending destinationPath with "file://" in GetAudioClip?
     
  27. valkirin2000

    valkirin2000

    Joined:
    Sep 27, 2022
    Posts:
    4
    Now it works!

    Thank you very much! You are saint!)
     
    yasirkula likes this.
  28. Beatdones

    Beatdones

    Joined:
    Mar 28, 2020
    Posts:
    7
    First of all, this is an awesome plugin! You are amazing for being willing to share this for free! Thanks a ton.

    Second, I'm trying to collect the dropdown value to know what type of file has been selected (image, video, document, etc).
    I need to know it to make a POST with the type of file that is being selected.

    If there is another way already implemented in the library I would love to know about it.

    Thank you very much for the great work.
     
    yasirkula likes this.
  29. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Hi! You can change FileBrowser.filters and FileBrowser.filtersDropdown to public and proceed from there.
     
  30. Beatdones

    Beatdones

    Joined:
    Mar 28, 2020
    Posts:
    7
    Thank you very much for the quick and efficient response. I appreciate it a lot.
     
    yasirkula likes this.
  31. ajavjari

    ajavjari

    Joined:
    May 23, 2019
    Posts:
    5
    I'm using this plugin for android. I'm not able to select file from download folder. It's showing "Can't use this folder to protect your privacy"
    Is there any workaround to use that folder?
    Screenshot_20230203_135211.jpg
     

    Attached Files:

  32. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    You're right, this is unfortunately a limitation of Android 12+ if I remember correctly. You can use NativeFilePicker to pick files from any folder, if you wish (Android & iOS only). Otherwise, this limitation can't be overcomed without declaring MANAGE_EXTERNAL_STORAGE permission (which would probably cause your app to be rejected by Google Play).
     
  33. vincyber

    vincyber

    Joined:
    Apr 1, 2017
    Posts:
    3

    Hi, a few years ago I asked you for a playmaker action to use your plugin and it worked great. Is it possible to have the same script as a visual scripting node in Unity?
     
  34. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Not at the moment unfortunately. I don't have any experience with the visual scripting system and don't have time to learn it for the time being.
     
  35. soundmartell

    soundmartell

    Joined:
    Oct 31, 2013
    Posts:
    16
    Hello, thank you for this good work.
    I am wondering if there is any way to force the browser to show only and only the Application.persistentDataPath directory.
    I don't want the user exploring all the other dir around but just the files .txt the game is creating in the Application.persistentDataPath
    Thank you for any help or support.
     
  36. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    @soundmartell Hi, you can deactivate the up button, path input field and quick links section from the SimpleFileBrowserCanvas prefab. Then, you should remove this section of the code. While showing the file browser, you can pass Application.persistentDataPath as initialPath.
     
  37. soundmartell

    soundmartell

    Joined:
    Oct 31, 2013
    Posts:
    16
    Thank you, I will try late.
     
    yasirkula likes this.
  38. unity_2451C087C01993D824AB

    unity_2451C087C01993D824AB

    Joined:
    Oct 8, 2022
    Posts:
    10
    Hello, thank you for your great work.
    I am new to unity so sorry If I ask dumb question.

    1) I checked the example code, and i wonder how to copy then paste file into streamingassets folder.

    select txt or json file
    copy and paste that selected file into streamingassets folder.

    if i click button, able to use that file.

    2) Is it possible to use in IOS?

    I would really appreciate for your help
     
  39. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Hi, you can simply use File.Copy(paths[0], Path.Combine(Application.streamingAssetsPath, Path.GetFileName(paths[0])), true); but it won't work on Android because Android's StreamingAssets folder resides inside its APK file. It will work on iOS AFAIK.
     
  40. unity_2451C087C01993D824AB

    unity_2451C087C01993D824AB

    Joined:
    Oct 8, 2022
    Posts:
    10
    Is it okay not to use FileBrowserHelpers?
    and is paths[0] simply just array?

    If i try

    string destinationPath = Path.Combine( Application.persistentDataPath, FileBrowserHelpers.GetFilename( FileBrowser.Result[0] ) );
    File.Copy(destinationPath, Path.Combine(Application.streamingAssetsPath, Path.GetFileName(destinationPath)), true);

    it cause FileNotFoundException: Could not find file Error
     
  41. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Why isn't destinationPath just FileBrowser.Result[0]? You don't need to use FileBrowserHelpers on any platform other than Android.
     
  42. unity_2451C087C01993D824AB

    unity_2451C087C01993D824AB

    Joined:
    Oct 8, 2022
    Posts:
    10
    Thank you so much for your kind work and fast reply.

    It works now :)
     
    yasirkula likes this.
  43. unity_2451C087C01993D824AB

    unity_2451C087C01993D824AB

    Joined:
    Oct 8, 2022
    Posts:
    10
    Hello, just in case to check.
    May i use your asset on commercial purpose?
     
  44. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Of course.
     
  45. Marcos-Elias

    Marcos-Elias

    Joined:
    Nov 1, 2014
    Posts:
    159
    I love your asset, it saved my game after Android 11!

    Now I’m trying to change all my functions that read files with your helpers. So users can access them directly on their selected folders instead of having to copy files to persistent data path. But I’m getting some weird results.

    It seems that the function that checks if directory exists is returning false even when they exist, so my app tries to create them again. They are created with numbers (2) (3) and so on. The game uses just the first one without the number. I’ve seen this happen also with many file managers after Android 11. Every time the verification is made it ends up with a new empty folder being created.

    What would be the best practices to verify if a directory exists?

    Can I add the normal / on paths, or should I use other character? With System.IO everything works even if we mix / and \.

    I saw that the path string returned on Android contains %2F for /. I’m also confused about the last /, should it exist or not during folder verifications?

    All my functions were using System.IO containing structures like those:

    …/folder/anotherfolder/

    …/folder/anotherfolder

    Since it depends on user input (mods) I must support both.

    Thanks in advance! Between many trials and errors your plug-in is one of the best for this task. Maybe it’s just me that skipped reading the entire code, sorry if I’m getting stuck with a very basic thing hehe.
     
  46. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    @Marcos-Elias You shouldn't manually append path components to the file paths returned by the plugin. Storage Access Framework doesn't work like that, unfortunately. You also can't check if a directory exists without the user first granting access to the directory or any of its parent directories via the "Browse" button. To see if a sub-directory exists, you need to call FileBrowserHelpers.GetEntriesInDirectory and see if any of the entries match the sub-directory. There is unfortunately no easier way for this, manually appending path components isn't a thing as I've said.
     
    Marcos-Elias likes this.
  47. Marcos-Elias

    Marcos-Elias

    Joined:
    Nov 1, 2014
    Posts:
    159
    Oh thanks a lot for your answer!

    Access to the first folder is granted already, but I must verify if all paths are valid or not because there are many mods with entries pointing to files that don't exist (we cannot rely on user input haha).

    I will keep the current implementation then, where users select a zip file and the game extracts the content to Application.persistentDataPath. Android is really bad now :(
     
  48. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    2,879
    Can't you use the GetEntriesInDirectory approach for path validation?
     
  49. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Installed this in Unity 2021.3.22+ and ... when I show the dialog, all the text is missing.

    On investigation:
    1. All your UnityUI.Text objects have null "font" reference
    2. In the prefab ... they all have a valid refernece ("Arial", unsurprisingly).

    Are you doing something, somewhere, where you override/replace the Font references dynamically at runtime (i.e. after creating the prefab?). I'm not changing fonts in the scene myself. If you're not doing it then maybe another plugin is - but why it would only affect this asset's prefabs, and no others (all other UnityUI objects in scene are fine, both from prefabs and non-prefab), is odd.
     
  50. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Ah, found it. Virgin install from the .unithpackage downloaded from your website just now: your LightSkin is corrupted, has "missing" for the Font reference.

    Re-importing the .unitypackage and overwriting LightSkin - same problem.

    Maybe an import bug in 2021.LTS?