UnityWebRequest for local files on Android - failure

I use UnityWebRequest to load images over the internet. The non-block native texture handling works well and everything is quite smooth.

Another very handy feature of UnityWebRequest is that it can actually load from HDD too, using file:// instead of http. This is very handy for caching as all you really need to do is alter the load path prefix to switch from a remote url to a local file path.

Only problem is it doesn't seem to work on Android.

Anyone out there able to get local UntyWebRequest image loading happening from an Android phone?

I added a bug report a month or so ago here if anyone is interested:

https://fogbugz.unity3d.com/default.asp?843412_h9ibu4sm4bld11g3

I have a similar problem.
UnityWebRequest for local files doesn't work on even Windows since 5.4.3p3.
I tried again 5.4.4p2. but It doesn't solved.

I found a workaround by accident.
Use url prefix file:/// instead of file://.

In the Unity 5.6, the UnityWebRequest for local files uses again the format "file://". If you use 3 slashes, it does not work.

If the first character of the path (Application.persistentDataPath for example) is "/" then you need three slashes. FYI for anyone dealing with that issue.

When using file:// URI the correct one is with three slashes:
- on UNIX-like operating systems absolute paths star with '/' symbol, so you have file:// + path there
- on Windows the correct URI is file:/// + path, it's a very common error to have only two slashes (and a lot of software accepts that too)

Also, it is not recommended to use UnityWebRequest for that. In most cases there are more efficient ways to read local files.

1 Like

We have never managed to get UnityWebRequest and a DownloadHandlerTexture to load files from StreamingAssets on Android, and don't understand why. This is with Unity 5.6.3f1, but we have tried many other 5.x versions. The same code has always worked fine with WWW, and runs on all platforms other than Android. Note for sanity, we have constructed the path string in two different ways, and checked that filePathPerDocs and filePathFromStreamingAssets are identical.

        // Construct path as recommended in https://docs.unity3d.com/Manual/StreamingAssets.html
        string filePathPerDocs = "jar:file://" + Application.dataPath + "!/assets/" + fileName;   
        Debug.Log("File Path per docs: " + filePathPerDocs);

        // Construct path as we always have for streaming assets.
string filePathFromStreamingAssets = Application.streamingAssetsPath + "/" + fileName;   
        if (Application.platform != RuntimePlatform.Android)
        {
            filePathFromStreamingAssets = "file://" + filePathFromStreamingAssets;
        }
        Debug.Log("File path using streamingassets: " + filePathFromStreamingAssets);

        // Load it. This will fail, but will work if using a WWW.
using (UnityWebRequest www = UnityWebRequest.GetTexture(filePathFromStreamingAssets))

On Android, both of these functions build a string of the form:

jar:file:///data/app/com.mycompany.application-nlg8YQn72iHpXna6k-FeqQ==/base.apk!/assets/imageToLoad.png

And fail with: "A URL Connection to a Java ARchive (JAR) file or an entry in a JAR file is not supported."

Can somebody tell me what is wrong with this string (e.g. by typing the correct form of the above string)? We have tried all manner of permutations of file://, ///, etc.

Interestingly, this thread implies that UnityWebRequest streamingassets support on Android was only added in 2017.1 (post #16)

https://discussions.unity.com/t/639586

Yet there are replies higher in this thread (e.g. #5) saying it works in 5.6. I see nothing in the 2017.1 release notes saying that android streamingassets support for UnityWebRequest was added in this version.

1 Like

Reading StreamingAssets on Android using UnityWebRequest is supported since 2017.1. It will not work in 5.6.
WWW supports this in 5.x.

1 Like

Thanks for clarifying. We’ll upgrade after our current release cycle.

@Aurimas-Cernius

Could you please state better ways to handle file operations. Let's say there is audio file/ video file or png/jpg on a local drive. I find UnityWebRequest & it's variety of download handlers to load the concerned file and make it Unity friendly.

My target is iOS && Android.

I have scoured the internet for a solid solution to load any Unity type file from local drive to Unity at runtime, to find none.

Thank you

1 Like

Texture and AudioClip have methods for loading from files.

Thanks all you guys for this wonderful thread this helped me

@Aurimas-Cernius could you be more specific, please? for instance, how to load a Texture2D during runtime (and not in the editor) with unity 2018.3? what is currently the best way and what you are referring to?

bear in mind that you need texture dimension information from that texture/image itself.

this is not an answer since width/height are static here: https://answers.unity.com/questions/432655/loading-texture-file-from-pngjpg-file-on-disk.html

1 Like

Maybe I am missing something, but how is this done for AudioClip?

2 Likes

@Aurimas-Cernius Can you be more specific for why the Texture2D.Apply is more efficient compared to
UnityWebRequestTexture.GetTexture?

I am trying to build a gallery of in-game screenshots and have tried numerous methods to retrieve the screenshots from disk. The user can take a screenshot, the script saves the file to disk. I want the thumbnail of the new screenshot to populate a gallery of the last 10 screenshots that the user can load.

Everything works, except that since this is in-game, the texture is not refreshed when written to the games folder. While working In the editor, AssetDatabase.Refresh() makes it work like I want, but I can't access that in a build.

Using the persistentDatapath and UnityWebRequest gives me the behavior that I want. However, I'm suspicious that the screenshots are now loaded in memory, even when the gallery is not displayed. The same would be true if I used the ScreencaptureAsTexture method. The manual recommends destroying the texture after it's been created. To me, that's the same as just accessing a texture using UnityWebRequest, so I don't see the point.

It isn’t in general. Measure your exact use case.
The advantage of UWR is that it creates texture on a background thread, so large part of work is done without stalling main thread.

We're experiencing what appears to be this same issue.

using (UnityWebRequest www = UnityWebRequest.Get(url)) {
    UnityWebRequestAsyncOperation asyncOp = www.SendWebRequest();
    while (asyncOp.isDone == false) {
        await Task.Delay(30);
    }
    if (www.isNetworkError || www.isHttpError) {
         Debug.LogWarning($"Network error whilst downloading [{url}] Error: [{www.error}]");
         return null;
    }
}

We have tested with a url as:

jar:file:///storage/emulated/0/Texture.png
file:///storage/emulated/0/Texture.png
/storage/emulated/0/Texture.png
storage/emulated/0/Texture.png

Every other combination we can think to try.

We consistently get HTTP/1.1 404 Not Found returned as the www.error

This could be a permissions issue, though we have external write permission. Or it could be an issue with the UnityWebRequest system. Or we could be missing something fundemental about Android file loading.

--

We understand that Application.dataPath and Application.persistentDataPath both exist (Though since neither path seems to be visible in a file explorer, we can't add our files there).

Our use case is that we need the access files that exist on our device outside the application folder. They will be placed there using a standard file explorer, ideally in a folder like "storage/CustomFolder/Texture.png".

There are lots of forum posts around this issue, but as is tradition, they all seem to go dead and remain unsolved.

1 Like

Sounds like a likely cause. Export Android Studio project from Unity and play with manifest settings.

The manifest in Android Studio includes

Various sources online state:
Note: If your app uses the WRITE_EXTERNAL_STORAGE permission, then it implicitly has permission to read the external storage as well.

Are you/ Is anyone aware of any other permissions or manifest keys that related to access?

It's worth nothing the the URI of the UnityWebRequest reads as http://localhost/storage/emulated/0, which I imagine is incorrect. Specifically using file:///storage/emulated/0 doesn't seem to make any difference (404).

Also noting I'm talking about primary internal storage, not an sd card, if that is likely to make a difference.

I remember doing this exact thing years ago through the old WWW class without half the headache :(

-

Any other pointers would be very appreciated.

1 Like