Mick79's avatar

I'm having a chronic bug that I cannot replicate but I know it's a real thing. Asking for help.

I have a product that over the past couple years has become used by tens of thousands of people. I know that many of these people use the app just fine but recently I am getting more and more people emailing me saying

"I can't upload tracks"

I put this down to user error at first but it's becoming too common. I cannot replicate it. No-one I know can replicate it but it is 100% definitely a thing. My logs show this error:

Invalid argument supplied for foreach()

I'm at the end of my tether. I cannot understand why it works fine for almost everyone but for a few folk it absolutely doesn't. Also, it should be noted that it's not just a random bug - if you're one of the people this affects then it NEVER works for you.

Here's my code

JAVASCRIPT - This code powers a file upload widget that also handles the upload and passes the data to my controller so that I can store info in the database

<script src="//static.filestackapi.com/filestack-js/3.x.x/filestack.min.js"></script>

<script>
    // Set up the picker
    const clientOptions = {
        security: {
            policy: "{{$uploadpolicy}}",
            signature: "{{$uploadsignature}}"
        }
    }

    const client = filestack.init("{{env('FILESTACK_KEY')}}", clientOptions);

    const options = {
        storeTo: {
            location: 's3',
            path: '/audio/'
        },
        uploadConfig: {
            tags: {
                "app": "xxxxxxxx"
            }
        },
        onUploadDone: updateForm,
        maxSize: {{env('PRO_FILESIZE_LIMIT')}} * 1024 * 1024,
        maxFiles: {{env('PRO_TRACK_LIMIT')}},
        accept: 'audio/*',
        uploadInBackground: true,
        transformations: {},

        onFileSelected(file) {
            if (file.size > {{env('PRO_FILESIZE_LIMIT')}} * 1024 * 1024) {
                throw new Error('File too big, select something smaller than 240MB');
            }
        }
    };

    const picker = client.picker(options);


    const form = document.getElementById('pick-form');
    const fileInput = document.getElementById('fileupload');
    const btn = document.getElementById('picker');
    const nameBox = document.getElementById('nameBox');
    const urlBox = document.getElementById('urlBox');


    btn.addEventListener('click', function (e) {
        e.preventDefault();
        picker.open();
    });


    function updateForm(result) {
        $("#loading-wrapper").show();
        var data = result.filesUploaded;
        $.post({
            type: 'POST',
            url: '/uploadtrack/{{$artist_token}}',
            data: {uploads: result.filesUploaded},
            // dataType: 'JSON',
            success: function () {
                console.log("Aye thats them uploaded");
                location.reload();
            }
        });
    }
</script>

and this is my controller that catches the above and stores the data in the database

public function uploadtrack(Request $request, $token)
    {

        $artist = Artist::query()->where('token', $token)->first();
        $artist_token = $artist->token;
        $account_token = $artist->account_token;
        $user = Auth::user();
        $userID = $user->id;
        Log::info("TULOGS - START OF NEW - TRACK UPLOAD BY: ".$userID);

        $membertype = $user->membertype;
        Log::info("TULOGS - USER: ".$userID." MEMBERTYPE: ".$membertype);

        $trackcount = Track::countTracks($userID);
        Log::info("TULOGS - USER: ".$userID." TRACKCOUNT: ".$trackcount);
        Log::info("TULOGS - USER: ".$userID." / GOT MEMBERTYPE AND TRACKCOUNT");

        if ($membertype == "Free") {
            $tracklimit = env('FREE_TRACK_LIMIT');

            if ($trackcount >= $tracklimit) {
                dd("Track limit reached, please upgrade");
            }
        }

        if ($membertype == "Hobby") {
            $tracklimit = env('HOBBY_TRACK_LIMIT');

            if ($trackcount >= $tracklimit) {
                dd("Track limit reached, please upgrade");
            }
        }


        Log::info("TULOGS - USER: ".$userID." / GOT THROUGH THE IF STATEMENTS");
        Log::debug("The request: ".$request);

// All the uploads that fail log out "no" from the following little test:
        if ($request->uploads) {
            Log::debug("Yes");
        } else {
            Log::debug("No");
        }

// this is where it's failing. for the ones that don't work $request->upload is not a thing - BUT WHY?!
        foreach ($request->uploads as $upload) {
            $filename = preg_replace('/\.[^.\s]{3,4}$/', '', $upload['filename']);
            $track = Track::create([
                'user_id' => $userID,
                'track' => $filename,
                'artist_token' => $artist_token,
                'account_token' => $account_token,
                'filesize' => $upload['size'],
                'tracksource' => $upload['url'],
                'count' => 0,
                'token' => uniqid($userID, true).uniqid(),
                'key' => $upload['key'],
            ]);
            $newtracks[] = $track;
            ProcessTrackWaveform::dispatch($track);
            Log::info("TULOGS - USER: ".$userID." / ".$track);

        }

        Log::info("TULOGS - USER: ".$userID." / GOT THROUGH THE FOREACH");

        session(['data' => $newtracks]);

        Log::info("TULOGS - USER: ".$userID." / STORED THE SESSION DATA");

        return 200;

    }


Apologies for the excessive use of Logs - I'm trying to debug this.

0 likes
8 replies
Snapey's avatar

when you throw an error in javascript, how does the user see it?

Snapey's avatar

@Mick79 ie,

throw new Error('File too big, select something smaller than 240MB');
jorgensolli's avatar

This has to be a filestack-related issue, right? For the people reporting it not working, have you asked them for information regarding their device/browser? I'm willing to bet that would give you a clearer picture of what's happening here.

On a side note, you should probably validate the request to gracefully let your users know if uploads is not an array.

achatzi's avatar

Maybe filestack fails to upload the files so you sent empty data in the controller. You could check the filesFailed part of the result (https://www.filestack.com/docs/uploads/pickers/web) as well

function updateForm(result) {
        $("#loading-wrapper").show();
        var data = result.filesUploaded;
		console.log(result.filesFailed);
        $.post({
            type: 'POST',
            url: '/uploadtrack/{{$artist_token}}',
            data: {uploads: result.filesUploaded},
            // dataType: 'JSON',
            success: function () {
                console.log("Aye thats them uploaded");
                location.reload();
            }
        });
    }
Mick79's avatar

@achatzi

This is a great shout. However not much use to me on the client side. Is there anyway to log this JS output to my actual back end logging using the Log function?

Tray2's avatar

@Mick79 Sure, create an API that is called when the error happens and store the information in a table.

achatzi's avatar

@mick79 At the js function, send that as well

function updateForm(result) {
        $("#loading-wrapper").show();
        var data = result.filesUploaded;

        $.post({
            type: 'POST',
            url: '/uploadtrack/{{$artist_token}}',
            data: {
				uploads: result.filesUploaded,
				failures: result.filesFailed
			},
            // dataType: 'JSON',
            success: function () {
                console.log("Aye thats them uploaded");
                location.reload();
            }
        });
    }

and at the controller

if ($request->failures) {
            Log::debug('failures', $request->failures);
        } else {
            Log::debug("No Failures");
        }
1 like

Please or to participate in this conversation.