Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

DevynKo's avatar

Livewire File Upload saying file is Null

It keeps saying that the file is null The file is very large, but I have updated php.ini to handle large files.

<?php

namespace App\Livewire;

use Livewire\Component;
use Livewire\WithFileUploads;
use Illuminate\Validation\Rule;
use App\Jobs\ImportSanmarDataLibrary;
use Illuminate\Support\Facades\Log;

class SanmarDataLibraryUploadForm extends Component
{
    use WithFileUploads;

    public $sanmar_file;

    protected $rules = [
        'sanmar_file' => '', // Adjust max size as needed
    ];

    public function uploadSanmarDataLibraryToDatabase()
    {
        Log::info('Received file:', ['sanmar_file' => $this->sanmar_file]);

        $this->validate();

        Log::info('File validation passed.', ['sanmar_file' => $this->sanmar_file]);

        try {
            // Store the file locally
            $path = $this->sanmar_file->store('uploads', 'local');

            Log::info('File stored locally at: ' . $path);

            // Concatenate the base path with the provided $path
            $fullPath = storage_path('app/' . $path);

            Log::info('Full file path: ' . $fullPath);

            // Enqueue the job to process the CSV file
            ImportSanmarDataLibrary::dispatch($fullPath);

            Log::info('Job dispatched to process the CSV file.');

            // Emit event to notify the upload is successful
            $this->dispatch('sanmar_file_uploaded');

            session()->flash('success', 'CSV file import queued successfully');
        } catch (\Exception $e) {
            Log::error('File upload failed: ' . $e->getMessage(), [
                'exception' => $e,
                'file' => $this->sanmar_file,
            ]);
            session()->flash('error', 'An error occurred during file upload: ' . $e->getMessage());
        }
    }


    public function render()
    {
        return view('livewire.settings.sanmar-data-library-upload-form');
    }
}

<div class="mt-2 flex flex-col space-y-4 items-start">
    @if (session()->has('success'))
        <div class="alert alert-success">
            {{ session('success') }}
        </div>
    @elseif (session()->has('error'))
        <div class="alert alert-danger">
            {{ session('error') }}
        </div>
    @endif

    <form wire:submit.prevent="uploadSanmarDataLibraryToDatabase" class="mt-6 space-y-6">
        @csrf
        <input type="file" wire:model="sanmar_file" name="sanmar_file"
            class="outline outline-2 -outline-offset-2 outline-gray-300 dark:outline-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-fuego-500 dark:focus:border-fuego-600 focus:ring-fuego-500 dark:focus:ring-fuego-600 rounded-md shadow-sm">
        @error('sanmar_file')
            <span class="text-red-500">{{ $message }}</span>
        @enderror

        <x-primary-button type="submit">Upload</x-primary-button>

        <div wire:loading wire:target="uploadSanmarDataLibraryToDatabase" class="mt-3 text-blue-500">
            Uploading...
        </div>
    </form>

    <x-action-message class="mt-3" on="sanmar_file_uploaded">
        {{ __('File Uploaded.') }}
    </x-action-message>
</div>
0 likes
10 replies
bvfi-dev's avatar
  • Could you paste your php.ini values for upload_max_filesize & post_max_size and also answer how big was the test file you are uploading?
  • What do you get when you add this:
public function updatedSanmarFile()
{
    Log::info('File has been updated:', ['sanmar_file' => $this->sanmar_file]);
}

It seems like there are events that you dispatch, and you should keep in mind that events in Livewire are asynchronous. This means that when you upload a file and then try to do some other action with that file, the file might not be fully uploaded yet, resulting in it being null.

I had a similar issue. If you need your actions to be synchronous (execute one after another), you should split your code into smaller functions and parts that can be called via events, ensuring they execute in the correct order. The dispatch() call should be the last thing in those functions.

Here's how I've been handling it:

  • Split the Code: Break down your logic into smaller methods.
  • Chain Events: Use events to chain these methods together in the correct sequence.
  • Ensure Order: Make sure the file upload completes before triggering any subsequent actions. Here's an example of how I handled a similar issue: Dealing with Livewire 3's Async Event Communication Between Components. The solution was: Breaking my code into smaller parts and ensuring events were called in sequence.
----------------------------------------------------
  • Another possibility is that you could use updatedSanmarFile() to handle the logic, because it would be called after the variable for the file upload has been changed. Note that this isn't called after the file has been fully uploaded, but when the binding variable for it was.
  • You could add indicators to see when the file was uploaded and when it finishes...I just realized those events are triggering changes in the front-end, I figured they were going into another Livewire Compoenent. Do your indicators work, because you have wire:target="uploadSanmarDataLibraryToDatabase"?
<div wire:loading wire:target="sanmar_file" class="mt-3 text-blue-500">
    Uploading...
</div>
<div wire:loading.remove wire:target="sanmar_file" class="mt-3 text-green-500">
    Upload complete.
</div>
DevynKo's avatar

@bvfi-dev

upload_max_filesize=800M
post_max_size=0
[2024-06-14 12:53:57] local.INFO: File has been updated: {"sanmar_file":{"Livewire\Features\SupportFileUploads\TemporaryUploadedFile":"C:\Users\devyn\AppData\Local\Temp\phpE8E4.tmp"}} 

It took about 2ish minutes for that output to finally show up in the log. Hmm I think its trying to store the file before it is fully put in the temp folder...

The file Im trying to upload is 151 MB (159,139,917 bytes).

NekaDava's avatar

Try adding this attribute to the form tag enctype="multipart/form-data"

<form wire:submit.prevent="uploadSanmarDataLibraryToDatabase" class="mt-6 space-y-6" enctype="multipart/form-data>

DevynKo's avatar

@snapey @nekadava I tried both your suggestions, but it did not fix the issue. I think it might be trying to store the file before the file it moved to the temp folder maybe? How could I fix that?

DevynKo's avatar

@bvfi I think im right on thinking its trying to store the file before it gets transfered to the tmp folder. I i click submit, at first it says the file is required/saying its null. but if I wait like 2 minutes, the updatedSanmarFile finally gets fired. If I click submit again the store/upload function works. How should I handle this situation, and how do I give the user feed back because the tricky thing is the updating lifecycle function also doesnt seem to fire until the file is finally transferred to the tmp folder.

bvfi-dev's avatar

@DevynKo Yea, I had a feeling it might be that. Honestly, Im a beginner as well and I don't want to mislead you with possibly bad suggestions, but I'll try to be as clear and practical as possible. Remember, this is how I would do it. I can't give you much code because this is not a simple task and you will need to handle a bunch of bugs as you develop it. I hope I can nudge you in the right direction with my sort of 'pseudo-code'

I would declare a public $isFileUploading = false in the Component that would disable all the actions that the user shouldn't be able to do while the file is uploading. Things like submitting the form, post-file-upload processing actions and stuff like that. So, when the user starts uploading a file you disable some buttons and actions.

Serverside Way

You could try and see if this would work, I dont know. This should be called before a property is being updated. Make sure you set it false in the updatedSandmarFile()

public function updatingSanmarFile()
{
    $this->isUploading = true;
}
  • Then you could use AlpineJS and depending on whether you have Livewire 2 or 3, you use entangle or $wire.entangle to bind AlpineJS with Livewire. So, you can bind it like (For Livewire v2):
<div x-data="{ isUploading: @entangle('isUploading') }" x-show="isUploading" class="mt-3 text-blue-500">
        Uploading...
    </div>
  • And then you just put whatever you want to be happenin in the updatedSanmarFile():
public function updatedSanmarFile()
{
    $this->isUploading = false;
    if(!is_null($this->sanmar_file)) {
        // Do validation logic here. If validation passes, do more logic, etc...
    }
}

I would first add a test button and check if updatingSanmarFile behaves the way you want. If it does, it's only a matter of using AlpineJS to bind it with the LivewireProperty and then handle the backend depending on the state of the property. You could also use:

finally {
    $this->isUploading = false;
}

as part of the try->catch->finally to set the uploading to false. The try/catch block should have ONLY 1 job in it, storing the file, nothing more.

  • Remember to split all your methods. Have a method do ONE JOB and one job only. A separate method for validating the file, saving the file in the DB, storing the file locally, try to split everything up, because this way you will future proof your Component for events, so that they can be sync or async and you would have much more control over them.
  • Remember that file uploads in Livewire are handled differently on the frontend side and the server side. On the server side it's difficult to interact with the frontend. When the a file is uploading, the server waits for it to upload and then it can try to handle it. While on the frontend, you can handle it using JS.
  • Use more checks, for example, you should do a check if $path = $this->sanmar_file->store('uploads', 'local'); is set before assigning it to $fullPath and similar. This way you would always know when an error is thrown and where the code fails and you could also enhance the user experience with this by throwing errors for them.

For a froentend way of handling the uploaded files, you need to dive deeper into AlpineJS or handle it with a JS script

1 like
DevynKo's avatar

@bvfi-dev Thank you for suggestions, I really appreciate you taking your time. The problem is the updatingSanmarFile(). It doesnt get fired until the file gets moved to the tmp folder. So im struggling figuring out how to give the user feedback that that file is uploading. It takes a minute or two after selecting a file, currently I just have a .live on input and once the updatedSanmarFile() fires then I store the file and do my logic.

bvfi-dev's avatar
bvfi-dev
Best Answer
Level 3

@DevynKo Like I said, you can use AlpineJS:

Remember that file uploads in Livewire are handled differently on the frontend side and the server side. On the server side it's difficult to interact with the frontend. When the a file is uploading, the server waits for it to upload and then it can try to handle it. While on the frontend, you can handle it using JS.

Since updatingSanmarFile() isn't working the way I would expect it to, I would say you do it through the frontend. Just use entangle (Untested code):

<div x-data="{ isUploading: @entangle('isUploading') }">
    <input type="file" wire:model="sanmar_file" name="sanmar_file" @change="isUploading = true" class...
    ....
    <div x-show="isUploading" class="mt-3 text-blue-500">
            Uploading...
        </div>

This should control the $isUploading and it would show the Uploading... only when isUploading is true Then in the Livewire component you initialize the value public $isUploading = false; and then set it false in the updatedSanmarFile()

Alternative way

  • I would write JS code x-on:change="handleFileUpload" (On the input element and then bind the variables there:
<script>
function handleImageChange(event) {
    const input = event.target;
    const file = input.files[0];
    const type = file.type;
    const size = file.size;
    //After performing file checks:
    this.$wire.set('isUploading', true);
    this.$wire.call('customComponentMethod'); //You can use this If you want to call a custom method as soon as the user chooses to upload
    ...

Here, you can write more special cases, you can immediately check If the file type is correct, and If not, prevent the upload. You could prevent users from uploading files that are above the limit and not even let it go into the tmp folder. Since you can upload large files, this is I think important. The problem is, the validation in Livewire comes AFTER the file was uploaded, maybe there is a Livewire way of handling it as soon as the file is being started to be uploaded, but I haven't discovered it yet.

Sorry, I can't help you out more with code, but I think I've given you enough resources to do this on your own. Use dd(); for quick debugging, or you can try Debugbar If you want child components to print out debug messages, otherwise they won't get caught/printed. I could write a whole File Upload component for you, but then you won't have learned anything, and in the future you would have no idea how to update it. I actually have to do the same thing for my upload component, so Im working in parallel with this issue :D

1 like

Please or to participate in this conversation.