webgl error on System.IO.StreamReader

Hello,

I’m trying to use this S3 method to simply get a file from S3 bucket:

public void GetObject(string fileName, string folderName, string downloadPath, object scriptable = null, string bucketNameS3 = "XXXX")
{
    string bucketFilePath = folderName.Length > 0 ? folderName + fileName : fileName;
    Debug.Log(string.Format("fetching {0} from bucket {1}",
        bucketFilePath, bucketNameS3));
    S3Client.GetObjectAsync(bucketNameS3, bucketFilePath, (responseObj) =>
    {
        byte[] data = null;
        var response = responseObj.Response;
        if (response.ResponseStream != null)
        {
            using (StreamReader reader = new StreamReader(response.ResponseStream))
            {
                using (MemoryStream memory = new MemoryStream())
                {
                    var buffer = new byte[4096];
                    var bytesRead = default(int);
                    while ((bytesRead = reader.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        memory.Write(buffer, 0, bytesRead);
                    }
                    data = memory.ToArray();
                    Debug.Log("Data to array");


                    if (OnComplete != null)
                    {
                        OnComplete(downloadPath + fileName, data, scriptable);
                    }
                }
            }
        }
    });
}

The callback then simply write the data:

File.WriteAllBytes(Application.streamingAssetsPath, data)

Everything works fine in my desktop build but when i try to execute the same in webgl, i get the following error:

ArgumentNullException: Value cannot be null.
Parameter name: stream
  at System.IO.StreamReader..ctor (System.IO.Stream stream, System.Text.Encoding encoding, System.Boolean detectEncodingFromByteOrderMarks, System.Int32 bufferSize, System.Boolean leaveOpen) [0x00000] in <00000000000000000000000000000000>:0

If you want to take a look, you can visit the application here and take a look to the console.

https://wakitowaki.github.io/blablabla/index.html

I really can not understand why. Please help me :slight_smile:

M.

This smells like a simple nullref.

How to fix a NullReferenceException error

https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

Steps to success:

  • Identify what is null
  • Identify why it is null
  • Fix that

Whatever it turns out to be, you must find a way to get the information you need in order to reason about what the problem is.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

If your problem would benefit from in-scene or in-game visualization, Debug.DrawRay() or Debug.DrawLine() can help you visualize things like rays (used in raycasting) or distances.

You can also call Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene manually, looking for all the parts, where they are, what scripts are on them, etc.

You can also call GameObject.CreatePrimitive() to emplace debug-marker-ish objects in the scene at runtime.

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target.

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

Once you get the code running, here is how to debug the actual network transaction:

Networking, UnityWebRequest, WWW, Postman, curl, WebAPI, etc:

And setting up a proxy can be very helpful too, in order to compare traffic:

It can’t be a null reference otherwise i would have recieved the same error in play mode in windows build…am i wrong? :slight_smile:

The browser runs in a completely different security context than a standalone app.

This might even be caused by something like CORS… have you investigated that?

nope. By the way…it sounds strange that this can have to deal with cors. The specific error output is related to SystemIO if i am not misunderstandig the whole mechanism. Maybe StreamReader is not usable from WebGL app?

You could never get at the filesystem from a WebGL build, but this is an already-created stream (presumably one already in memory) that you’re getting as a callback from the S3 stuff right?

At a minimum, print out what’s coming to you from S3, such as responseObj, so you can see if that object or parts of it are null.

Are you sure the S3 library you’re using works in WebGL?

We’ve always just hit our S3 buckets as public and used good old UnityWebRequest() without any authorization, just publicly-facing data such as new levels for our games or new artwork or whatever.

Yes i’m in the same situation…is fine to use unitywebrequest. I already have the public bucket…how do i have to change my code to do what you are describing? can you modify my code directly?

No, but you can take these steps:

  • get the correct public URL off your S3 admin page

  • open it in a browser and VERIFY it actually downloads the file perfectly

  • go learn about UnityWebRequest and how to download arbitrary files and use them in Unity.

i really would like to thank you for your time…but if i am here, it is because i’ve already searched everywhere. So, if your answer to my question is “go and study”, well thanks for the advice but this is quite unuseful. I’m an intermediate user and i’m writing here to have some practical help and some pieces of code in order to understand where are my mistake.

By the way, thanks for your tips and references.

It’s hard to disect your error since we have no line numbers or any concrete information where the error actually happens. What I can say for sure is that this line will never work:

File.WriteAllBytes(Application.streamingAssetsPath, data)

The streaming assets path is an url on your server. You can not use the file system to “write to an url”. Yes, in standalone builds the streaming assets path is just a folder on disk and you can happily read and write files there with ordinary fileIO. This does not work when running inside a browser. A website does not have any access to the local file system. Any interaction with your server has to be done through webrequests. In order to upload data to your server, the server has to have some sort of server-side API that you can use and that is actually accepting uploaded data. Of course this is a huge security concern because people could upload anything to your server if not protected properly.

Note that even inside a webGL build you can use System.IO.File stuff, however not as you may think. Quite recently (on a larger time scale) webbrowsers will actually provide a virtual file system exclusively for your website on the users PC. All this, alongside with cookies, can be summed up as “web storage”. For WebGL builds the only real relevant path is the persistent data path. It gives you a virtual path in order to store files in the browser. Of course those files only exist on the users PC and can only be accessed by code of your own website.

When using firefox (or crome i think) you can open the web developer tools and you will find a tab exclusively for web storage. Anything that the website has stored on your machine will be visible there.

I visited your page, but I can’t really see that error you mentioned. When I enter any of those 18 “doors” I get an error saying that it can not load the scene “Runtime” because it was not added to the build settings. So it seems your build is incomplete. Maybe that’s on purpose, however I can’t seem to figure out how to actually trigger the error you’re talking about.

i am sorry, i forget to add that in order to fire the method and to generate the error, you have to press space on the main lobby (where the login panel is).

by the way, i simplified the code in order to be more clear:

public Text ResultText = null;

        void Start()
        {
            UnityInitializer.AttachToGameObject(this.gameObject);
            GetBucketListButton.onClick.AddListener(() => { GetBucketList(); });
            PostBucketButton.onClick.AddListener(() => { PostObject(); });
            GetObjectsListButton.onClick.AddListener(() => { GetObjects(); });
            DeleteObjectButton.onClick.AddListener(() => { DeleteObject(); });
            GetObjectButton.onClick.AddListener(() => { GetObject(); });
        }

public void GetBucketList()
{
    ResultText.text = "Fetching all the Buckets";
    Client.ListBucketsAsync(new ListBucketsRequest(), (responseObject) =>
    {
        ResultText.text += "\n";
        if (responseObject.Exception == null)
        {
            ResultText.text += "Got Response \nPrinting now \n";
            responseObject.Response.Buckets.ForEach((s3b) =>
            {
                ResultText.text += string.Format("bucket = {0}, created date = {1} \n", s3b.BucketName, s3b.CreationDate);
            });
        }
        else
        {
            ResultText.text += "Got Exception \n";
        }
    });
}

So now i am not even using any function to write on disk like you suggested. But still, if you try to click on list buckets (the only button that should work) it is giving me the stream error…

Well, then the error will most likely be inside the ListBucketsAsync function or inside the property that is constructing the response object when accessing it. Is this supposed to make a webrequest behind the scenes? I can’t see any network activity when I click the button. So the request may not even go out and return with an error internally. So when it tries to parse the result internally it hits that issue that there is no stream to read from. So you have to look into that.

I have never used the S3 server or any of that stuff. However according to this sample code you should / could be checking the responseObject.Exception to detect an error. By ignoring the built-in error handling and just blindly reaching for the potential response would yield a useless error such as the one you got.

edit
I just realised you literally used the example in your latest post ^^. So maybe this is still related to a failed login / authentication.