To Download Image from Url

Read the Thread title as “To Download Image from external URL to the current domain

I tried the 2 usual methods : UnityWebRequest and WWW.

As below sample code are both working from editor :

 private IEnumerator GetOnlineImage(string url)
    {
        UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url);
        yield return uwr.SendWebRequest();

        if (uwr.result != UnityWebRequest.Result.Success)
        {
            string log = uwr.error + "(" + url + ")";
            Debug.Log(log);
        }
        else
        {
            textureFromUrl = ((DownloadHandlerTexture)uwr.downloadHandler).texture;
        }
    }
 private IEnumerator GetOnlineImage1(string url)
    {
        var www = new WWW(url);
        yield return www;
        textureFromUrl = www.texture;

        www.LoadImageIntoTexture(textureFromUrl);
    }

By chance is there any we could do? :frowning:

Toying a bit with the jslib…

Some function to download text file runs fine :

    DownloadText: function (filename, text)
    {
        // Source : https://stackoverflow.com/a/30139435

        var element = document.createElement('a');
        element.setAttribute('href', 'data:text/text;charset=utf-8,' + encodeURI(String(text)));
        element.setAttribute('download', String(filename) + ".txt");
        element.click();
    },

However for some other code to download an image from an url :
// source : how to download image from url in javascript code example

var a = $("<a>").attr("href", "http://i.stack.imgur.com/L8rHf.png").attr("download", "img.png").appendTo("body");

a[0].click();

a.remove();

Unity WebGL player reports a " ReferenceError : $ is not defined"

What does the $ refers to?
I am really not familiar with JavaScript at all… o_o.

Doing some more progress using XMLHttpRequest…

    DownloadImageFromUrl: function (url, callback)
    {
        // Source : https://stackoverflow.com/a/43015238

        toDataUrl = function (url, callback)
        {
            var xhr = new XMLHttpRequest();
            xhr.onload = function () {
                var reader = new FileReader();
                reader.onloadend = function ()
                {
                    window.alert("Before Callback");
                    Runtime.dynCall('v', callback, [reader.result]);
                    //callback(reader.result);
                }
                reader.readAsDataURL(xhr.response);
            };
            xhr.open('GET', url);
            xhr.responseType = 'blob';
            xhr.send();
        }

        toDataUrl(Pointer_stringify(url), callback);
        window.alert("On Progress");
    },

I am having a bunch of error from the console… any idea?..

You can test out the sample here :

  • Unity WebGL Player | Test
    Press 1,2 or 3 to select an url, then press Save.
    Check out the Console to get the same error logs.

I think you can’t load something from a different url.
There is no error (just a info/issue) if you try to load from the same webpage.
https://a.samavan.com/unity/webtest/TemplateData/fullscreen-button.png e.g.

I use this to load images from a sub-folder in the same directory:

       //string MYURL = Application.streamingAssetsPath + "/subfolder/" + filename
       //yield return Helper.LoadTextureFromUrl(MYURL, true, returnValue => { });

        //in my Helper.cs
        public static IEnumerator LoadTextureFromUrl(string url, bool nonReadable, System.Action<Texture2D> callback)
        {
            _webRequestInProgress = true;
            using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url, nonReadable))
            {
                yield return uwr.SendWebRequest();
                if (uwr.result == UnityWebRequest.Result.ConnectionError || uwr.result == UnityWebRequest.Result.ProtocolError) Debug.Log("LoadImageFile failed: " + uwr.error);
                else
                {
                    Texture2D texture = DownloadHandlerTexture.GetContent(uwr);
                    texture.name = url.Split('/').Last().Split('.').First();
                    callback(texture);
                }
            }
            _webRequestInProgress = false;
        }

Well that is what I actually showed in my first post…
But that’ really bugging me, that it cannot access to a different url.
I am digging into the Javascript, I’ll share if anything coming out…

Finally got positive result downloading an external url doing my request from the html using ajax.
Unity > jslib > ajax call from html.
It seems this web it goes free from the webGL constraints.

Aright then I found a solution to download files from Unity which aren’t host on the same domain.
I share it in case it can be improved and help some :slight_smile:

From Unity C# :

  [DllImport("__Internal")]
    private static extern string DownloadFileFromUrl(
        string url,
        string objSource,
        string msgOnProgress,
        string msgOnLoaded,
        string msgOnError);

  public void Start()
    {
        DownloadFileFromUrl(
            inputUrl.text,
            gameObject.name,
            "OnDataDownloadProgress",
            "OnDataDownloadSuccess",
            "OnDataDownloadError");
    }

    public void OnDataDownloadProgress(float progress)
    {
    }
    public void OnDataDownloadSuccess(string base64)
    {
    }

    public void OnDataDownloadError(string msg)
    {
    }

From Unity *.jslib :

mergeInto(LibraryManager.library,
{
 DownloadFileFromUrl: function (url, objSource, msgOnProgress, msgOnLoaded, msgOnError)
    {
        AjaxLoaderFromHtml(Pointer_stringify(url),
            Pointer_stringify(objSource),
            Pointer_stringify(msgOnProgress),
            Pointer_stringify(msgOnLoaded),
            Pointer_stringify(msgOnError));
    },
});

From here to have a custom WebGLTemplates and to include the following external.js script :
(base ajax code by Cyril Pereira : https://med.planet-d.net/)

function NewAjaxLoader(url, objSource, msgOnProgress, msgOnLoaded, msgOnError) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.responseType = "arraybuffer";
    xhr.onprogress = function (e) {
        if (e.lengthComputable) {
            var progress = Math.round((e.loaded / e.total) * 100);
            console.log("progress : " + String(progress));
            unityInstance.SendMessage(objSource, msgOnProgress, progress);
        }
    };
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            var arrayBuffer = xhr.response;
            var base64String = btoa(
                String.fromCharCode.apply(null, new Uint8Array(arrayBuffer))
            );
            unityInstance.SendMessage(objSource, msgOnLoaded, base64String);
        }
    };
    xhr.onerror = function (e) {
        unityInstance.SendMessage(objSource, msgOnError, String(e));
    };
    return xhr;
}

Finally from the index.html file :

       <script src='external.js'></script>
<script>
function AjaxLoaderFromHtml(url, objSource, msgOnProgress, msgOnLoaded, msgOnError)
    {
        NewAjaxLoader(url, objSource, msgOnProgress, msgOnLoaded, msgOnError).send();
    }
</script>

It’s very important the ajax.send() is made from the *.html if you want to escape the external Javascript request being blocked by webGL.

Voila :wink:

3 Likes

very interessing

1 Like

@sama-van Thank you so much it’s working perfectly!

1 Like