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

MetalKaiser's avatar

[UPDATE]Problem using maatwebsite/excel: C:\Users\<My user>\AppData\Local\Temp\laravel-excel-k5R8qyVIzkH8X0m5YwHyC2IOznrThvdk is an Invalid HTML file

I'm trying to make an excel export via maatwebsite/excel, but I keep getting the following error:

C:\Users\<My Laravel project directory>\vendor\phpoffice\phpspreadsheet\src\PhpSpreadsheet\Reader\Html.php:592

public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)

{

    // Validate

    if (!$this->canRead($pFilename)) {

        throw new Exception($pFilename . ' is an Invalid HTML file.');

    }
}

My Controller:

return Excel::download(new ContractsExport($contracts, $active), 'solic_contrat_' . date('d-m-Y') . '_' . time() . '.xlsx');

My export file:

<?php

namespace App\Exports;

use App\Contract;
#use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\Exportable;
use Illuminate\Contracts\View\View;
use Maatwebsite\Excel\Concerns\FromView;

class ContractsExport implements FromView
{
    use Exportable;

    protected $contracts;
    protected $active;

    public function __construct($contracts = null, $active = null)
    {
        $this->contracts = $contracts;
        $this->active = $active;
    }

    public function view(): View
    {
        $contracts = $this->contracts;
        $active = $this->active;
        return view("exports.contracts", compact("contracts", "active"));
    }
}

The blade template:

<table>
    <thead>
        <tr>
            <th><b>N°</b></th>
            <th><b>RUT</b></th>
            <th><b>Apellido Paterno</b></th>
            <th><b>Apellido Materno</b></th>
            <th><b>Nombre</b></th>
            <th><b>Cargo</b></th>
            <th><b>Fecha de inicio</b></th>
            <th><b>Fecha de término</b></th>
            <th><b>Causal de contratación</b></th>
            <th><b>Tope del causal</b></th>
            <th><b>Lugar de trabajo</b></th>
            <th><b>Estado</b></th>
        </tr>
    </thead>
    <tbody>
        @php
        $k = 0;
        @endphp
        @foreach ($contracts as $contract)
        @php
        $k++;
        @endphp
        <tr>
            <td>{{ $k }}</td>
            <td>{{ $contract->rut }}</td>
            <td>{{ $contract->f_lname }}</td>
            <td>{{ $contract->m_lname }}</td>
            <td>{{ $contract->name }}</td>
            <td>{{ $contract->search->position->name }}</td>
            @php
            $startdate = new \Carbon\Carbon($contract->search->start_date);
            @endphp
            <td>{{ $startdate->format('d/m/Y') }}</td>
            @php
            $enddate = new \Carbon\Carbon($contract->search->date);
            @endphp
            <td>{{ $enddate->format('d/m/Y') }}</td>
            <td>{{ $contract->search->causal_service }}</td>
            @switch($contract->search->causal_service)
            @case('Reemplazo por motivo de licencia médica')
            @case('Reemplazo por motivo de vacaciones')
            <td>Sin tope de días</td>
            @break
            @case('Proyecto nuevos y específicos')
            @php
            $date = $contract->search->start_date;
            echo '<td>' . date('d/m/Y', strtotime($date. '+ 180 days')) . '</td>';
            @endphp
            @break
            @case('Trabajos urgentes')
            @case('Evento extraordinario')
            @case('Aumento ocasional')
            @php
            $date = $contract->search->start_date;
            echo '<td>' . date('d/m/Y', strtotime($date. '+ 90 days')) . '</td>';
            @endphp
            @break
            @endswitch

            <td>{{ $contract->search->address }}</td>

            @switch($contract->status)
            @case('pending')
            <td>Por Aprobar</td>
            @break
            @case('approved')
            <td>Aprobada</td>
            @break
            @case('rejected')
            <td>Rechazada</td>
            @break
            @case('cancelled')
            <td>Cancelada</td>
            @break
            @case('process')
            <td>En Proceso</td>
            @break
            @default
            <td>Estado no permitido</td>
            @endswitch

        </tr>
        @endforeach
    </tbody>

</table>

And the result (laravel-excel-k5R8qyVIzkH8X0m5YwHyC2IOznrThvdk):

<table>
    Solicitudes de Contrataciones
    <thead>
        <tr>
            <th><b>N°</b></th>
            <th><b>RUT</b></th>
            <th><b>Apellido Paterno</b></th>
            <th><b>Apellido Materno</b></th>
            <th><b>Nombre</b></th>
            <th><b>Cargo</b></th>
            <th><b>Fecha de inicio</b></th>
            <th><b>Fecha de término</b></th>
            <th><b>Causal de contratación</b></th>
            <th><b>Tope del causal</b></th>
            <th><b>Lugar de trabajo</b></th>
            <th><b>Estado</b></th>
        </tr>
    </thead>
    <tbody>
                                <tr>
            <td>1</td>
            <td>20.284.123</td>
            <td>Alonso</td>
            <td>Gonzalez</td>
            <td>Daniela</td>
            <td>Ejecutivo de Ventas</td>
                        <td>23/11/2019</td>
                        <td>25/12/2019</td>
            <td>Trabajos urgentes</td>
                                                <td>21/02/2020</td>            
            <td>Esperanza 77, Santiago</td>

                        <td>En Proceso</td>

        </tr>
                        <tr>
            <td>2</td>
            <td>22.143.965</td>
            <td>Moreno</td>
            <td>Vega</td>
            <td>Jesús</td>
            <td>Secretaria</td>
                        <td>20/11/2019</td>
                        <td>25/12/2019</td>
            <td>Evento extraordinario</td>
                                    <td>18/02/2020</td>            
            <td>Apoquindo 4000, Las Condes</td>

                        <td>Por Aprobar</td>

        </tr>
                        <tr>
            <td>3</td>
            <td>25.334.235</td>
            <td>Pérez</td>
            <td>Rodriguez</td>
            <td>Juan</td>
            <td>Secretaria</td>
                        <td>20/11/2019</td>
                        <td>25/12/2019</td>
            <td>Evento extraordinario</td>
                                    <td>18/02/2020</td>            
            <td>Apoquindo 4000, Las Condes</td>

                        <td>Por Aprobar</td>

        </tr>
            </tbody>

</table>

I'm using "maatwebsite/excel": "^3.1", and there must be something I'm missing from the Laravel-excel documentation, but right now I'm just walking in circles.

Thanks in advance to anyone who wishes to help me.

0 likes
11 replies
Nakov's avatar

@metalkaiser is this the full Blade template or you have something else there.

<table>
    Solicitudes de Contrataciones
    <thead>

How does this text comes in from the compiled file?

Make sure you just have the table within your .blade.php file, and make sure that you have closing tags for everything. Try it with a static table before you do yours just to check if it does export. Then I would move some of the logic, for the status for example within the Model in an accessor method, and not do it in the view.

public function getTranslatedStatusAttribute()
{
    $status = $this->status;
    if($status === 'pending')
    {
        return 'Por Aprobar';
    }
    elseif($status === 'approved')
    {
        return 'Aprobada';
    }
    elseif($status === 'rejected')
    {
        return 'Rechazada';
    }
    elseif($status === 'cancelled')
    {
        return 'Cancelada';
    }
    elseif($status === 'process')
    {
        return 'En Proceso';
    }
    
    return 'Estado no permitido';
}

and use it in the view:

<td> {{ $contract->translated_status }} </td>
MetalKaiser's avatar

Hi @nakov, thanks for your reply.

I tried with a static table and the issue persists.

My .blade.php:

<table>
    <thead>
        <tr>
            <th><b>N°</b></th>
            <th><b>RUT</b></th>
            <th><b>Apellido Paterno</b></th>
            <th><b>Apellido Materno</b></th>
            <th><b>Nombre</b></th>
            <th><b>Cargo</b></th>
            <th><b>Fecha de inicio</b></th>
            <th><b>Fecha de término</b></th>
            <th><b>Causal de contratación</b></th>
            <th><b>Tope del causal</b></th>
            <th><b>Lugar de trabajo</b></th>
            <th><b>Estado</b></th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1</td>
            <td>20.284.123</td>
            <td>Alonso</td>
            <td>Gonzalez</td>
            <td>Daniela</td>
            <td>Ejecutivo de Ventas</td>
            <td>23/11/2019</td>
            <td>25/12/2019</td>
            <td>Trabajos urgentes</td>
            <td>21/02/2020</td>            
            <td>Esperanza 77, Santiago</td>
            <td>En Proceso</td>
        </tr>
        <tr>
            <td>2</td>
            <td>22.143.965</td>
            <td>Moreno</td>
            <td>Vega</td>
            <td>Jesús</td>
            <td>Secretaria</td>
            <td>20/11/2019</td>
            <td>25/12/2019</td>
            <td>Evento extraordinario</td>
            <td>18/02/2020</td>            
            <td>Apoquindo 4000, Las Condes</td>
            <td>Por Aprobar</td>
        </tr>
        <tr>
            <td>3</td>
            <td>25.334.235</td>
            <td>Pérez</td>
            <td>Rodriguez</td>
            <td>Juan</td>
            <td>Secretaria</td>
            <td>20/11/2019</td>
            <td>25/12/2019</td>
            <td>Evento extraordinario</td>
            <td>18/02/2020</td>            
            <td>Apoquindo 4000, Las Condes</td>
            <td>Por Aprobar</td>
        </tr>
    </tbody>
</table>

The same result:

PhpOffice\PhpSpreadsheet\Reader\Exception C:\Users\Kaiser\AppData\Local\Temp\laravel-excel-O04tssILGv1pfV9Uor95vrBu4opUhL4K is an Invalid HTML file.

public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet)

    {

        // Validate

        if (!$this->canRead($pFilename)) {

            throw new Exception($pFilename . ' is an Invalid HTML file.');

        }

 

       ....

    }

I'm very confused by this result

Nakov's avatar

@metalkaiser

what about trying this table instead, so in your blade view:

<table>
    <thead>
    <tr>
        <th>#</th>
        <th>Apellido Paterno</th>
    </tr>
    </thead>
    <tbody>
    @foreach($contracts as $contract)
        <tr>
            <td>{{ $contract->id }}</td>
            <td>{{ $contract->f_lname }}</td>
        </tr>
    @endforeach
    </tbody>
</table>

simple as this.. So you can find the invalid part after adding each header and column. Step by step :)

MetalKaiser's avatar

Same error everytime. I even tried with a bit simpler static table:

<table>
  <tr>
    <th>Firstname</th>
    <th>Lastname</th>
    <th>Age</th>
  </tr>
  <tr>
    <td>Jill</td>
    <td>Smith</td>
    <td>50</td>
  </tr>
  <tr>
    <td>Eve</td>
    <td>Jackson</td>
    <td>94</td>
  </tr>
</table>

I'm suspecting the error is not related to the .blade.php file. Maybe something more related to Maatwebsite\Excel?

MetalKaiser's avatar

[Update] I made a test by directly declaring the view with a static table on the controller:

 Excel::download(view("exports.contracts"), 'solic_contrat_' . date('d-m-Y') . '_' . time() . '.xlsx');

The export was "successful", but the .xlsx file is empty.

Nakov's avatar

@metalkaiser so it means that it was not successful :)

btw, I just saw in your ContractsExport you use Exportable trait. Why is that?

I didn't see it used in the documentation when using FromView. Just try with a collection, and you'll see it is much easier :)

MetalKaiser's avatar

I tried with collection and using Contract::all() and it worked, but without the proper table headers and info.

That's basically the challenge here :-/

Nakov's avatar
Nakov
Best Answer
Level 73

@metalkaiser not a challenge at all :)

This is what I do, you can override the headers function within the export class:

    public function headings(): array
    {
        return [
            '#',
            'Name',
            'Last name',
        ];
    }

You will add all of your headers in it..

And the collection instead of returning just all() you can return columns based on the headers:

public function collection()
    {
        return Contract::all()->map(function($contract) {
     
           return [
               '#' => $contract->id, 
                // .... other columns
           ];

       });
    }
MetalKaiser's avatar

Sorry for the long wait @nakov

Your advice worked nicely. You just forgot to mention I need to add use Maatwebsite\Excel\Concerns\WithHeadings; to the export file.

I actually have my export like this:

<?php

namespace App\Exports;

use App\Contract;
use App\ContractHistory;
use App\ActiveContract;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\Exportable;
use Illuminate\Contracts\View\View;
#use Maatwebsite\Excel\Concerns\FromView;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;

class ContractsExport implements FromCollection, WithHeadings, ShouldAutoSize, WithEvents
{

    use Exportable;

    protected $contractids;
    protected $active;

    public function __construct($contractids = null, $active = null)
    {
        $this->contractids = $contractids;
        $this->active = $active;
    }

    /*public function view(): View
    {
        $contracts = $this->contracts;
        $active = $this->active;
        return view("exports.contracts", compact("contracts", "active"));
    }*/
    public function headings(): array
    {
        return [
            '#',
            'RUT',
            'Apellido paterno',
            'Apellido materno',
            'Nombre',
            'Cargo',
            'Fecha de inicio',
            'Fecha de término',
            'Causal de contratación',
            'Tope del causal',
            'Lugar de trabajo',
            'Cliente',
            'Estado',
        ];
    }

    public function collection()
    {
        switch ($this->active) {
            case 'Solicitudes de Contrataciones':
                $dbtable = Contract::whereIn('status', ['pending', 'approved', 'process'])
                ->whereIn('id', $this->contractids);
                break;
            case 'Contrataciones activas':
                $dbtable = ActiveContract::where('status', "active");
                break;
            case 'Historial de contrataciones':
                $dbtable = ContractHistory::whereIn('status', ['']);
                break;
        }
        return $dbtable->get()->map(function($contract) {
            if ($this->active == "Solicitudes de Contrataciones") {
                $startdate = $contract->search->start_date;
                $enddate = $contract->search->end_date;
            }else {
                $startdate = $contract->start_date;
                $enddate = $contract->end_date;
            }
            switch ($contract->status) {
                case 'pending':
                    $status = "Por aprobar";
                    break;
                case 'approved':
                    $status = "Aprobado";
                    break;
                case 'process':
                    $status = "En proceso";
                    break;
                case 'cancelled':
                    $status = "Cancelada";
                    break;
                case 'rejected':
                    $status = "Rechazada";
                    break;
                case 'completed':
                    $status = "Conpletada";
                    break;
                case 'nullified':
                    $status = "Anulada";
                    break;
            }
            return [
                '#' => $contract->id,
                'RUT' => $contract->rut,
                'Apellido paterno' => $contract->f_lname,
                'Apellido materno' => $contract->f_lname,
                'Nombre' => $contract->name,
                'Cargo' => $contract->search->position->name,
                'Fecha de inicio' => $startdate,
                'Fecha de término' => $enddate,
                'Causal de contratación' => $contract->search->causal_service,
                'Tope del causal' => $contract->name,
                'Lugar de trabajo' => $contract->search->address,
                'Cliente' => $contract->search->client->name,
                'Estado' => $status,
            ];

       });
    }

    public function registerEvents(): array
    {
        return [
            AfterSheet::class    => function(AfterSheet $event) {
                $cellRange = 'A1:W1'; // All headers
                $event->sheet->getDelegate()->getStyle($cellRange)->getFont()->setSize(13);
            },
        ];
    }
}

I still need to make some adjustments for making it work in every .blade.php I need it, but it's now working.

Thank you very much for your help.

MetalKaiser's avatar

I found out the origin of the issue: I saved my .blade.php file with coding UTF-8 with BOM.

Saving it with UTF-8 solved the issue.

Please or to participate in this conversation.