skip to Main Content

I have this PHP script (process.php):

<?php

// Include necessary libraries for database, PDF generation, and ZIP compression.
require_once('db_connection.php');
require_once('tcpdf/tcpdf.php');

if (isset($_POST['submit'])) {
    // Get uploaded file data
    $csvFile = $_FILES['csv_file']['tmp_name'];
    
    // Initialize progress variables
    $totalRecords = count(file($csvFile)) - 1; // Subtract 1 for the header row
    $processedRecords = 0;
    
    // Create a temporary directory to store the PDF files
    $tempDir = '/var/www/html/insuromatic/temp/';
    if (!file_exists($tempDir)) {
        mkdir($tempDir, 0777, true);
    }

    // Create a ZipArchive instance
    $zip = new ZipArchive();
    $zipFileName = 'pdf_archive.zip';

    if ($zip->open($zipFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
        // Open and read the CSV file
        if (($handle = fopen($csvFile, "r")) !== FALSE) {
            // Skip the first row (header)
            fgetcsv($handle);
            
            while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
                // Assuming your CSV columns are in order (column1, column2, column3)
                $column1 = $data[0];
                $column2 = $data[1];
                $column3 = $data[2];

                // Insert data into the MySQL database
                $sql = "INSERT INTO import_csv_data (id, name, email) VALUES ('$column1', '$column2', '$column3')";
                mysqli_query($conn, $sql);

                // Generate a PDF for this record
                $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
                $pdf->AddPage();
                $pdf->SetFont('helvetica', '', 12);
                $pdf->Cell(0, 10, "ID: $column1", 0, 1);
                $pdf->Cell(0, 10, "Naam: $column2", 0, 1);
                $pdf->Cell(0, 10, "Email: $column3", 0, 1);
                // Customize the PDF content as needed

                // Save the PDF in the temporary directory
                $pdfFileName = $tempDir . "record_$column1.pdf";
                $pdf->Output($pdfFileName, 'F');
                
                // Add the PDF to the ZIP archive
                $zip->addFile($pdfFileName, "record_$column1.pdf");

                // Delete the record from the database
                $deleteSql = "DELETE FROM import_csv_data WHERE id = '$column1'";
                mysqli_query($conn, $deleteSql);
                
                // Update progress
                $processedRecords++;
                                
                // Send progress to the client
//              echo "Processed $processedRecords out of $totalRecords records.<br />n";   // LINE 65

                // Flush the output buffer to send data immediately to the client
                ob_flush();
                flush();

                // Close the PDF document
                $pdf->Close();
            }
            fclose($handle);
        }

        // Close the ZIP archive
        $zip->close();

        // Remove temporary PDF files
        array_map('unlink', glob($tempDir . '*.pdf'));
        rmdir($tempDir);

        // Provide the ZIP archive for download
        header("Content-Type: application/zip");
        header("Content-Disposition: attachment; filename="$zipFileName"");
        ob_end_clean();
        flush();
        readfile($zipFileName);   // LINE 89
        unlink($zipFileName); // Delete the ZIP file after download
    }

    // Close the MySQL connection
    mysqli_close($conn);
}
?>

Server config:

Apache/2.4.57 (Debian)
PHP 8.0.30 FPM/FastCGI

Problem:

  • The echo() statement on line 65 works fine when the readfile() statement on line 89 is commented out, I see the progress echoed on the screen
  • The readfile() statement on line 89 works fine when the echo() statement on line 65 is commented out, I get a ZIP download containing the created PDF files

However, when both lines are enabled, I see a lot of garbage on the screen (the raw PDF content I think) after the progress is echoed to the screen. The ZIP is not offered for download.

Unfortunately, I have no clue about what i’m doing wrong. I tried several places of ob_end_clean(), ob_end_flush() and so on but with no luck, the garbage keeps being printed. Can someone point me in the right direction?

//Edit: reworked version:

<?php

// Include necessary libraries for database, PDF generation, and ZIP compression.
require_once('db_connection.php');
require_once('tcpdf/tcpdf.php');

if (isset($_POST['submit'])) {
    // Get uploaded file data
    $csvFile = $_FILES['csv_file']['tmp_name'];

    // Initialize progress variables
    $totalRecords = count(file($csvFile)) - 1; // Subtract 1 for the header row
    $processedRecords = 0;

    // Create a temporary directory to store the PDF files
    $tempDir = '/var/www/html/insuromatic/temp/';
    if (!file_exists($tempDir)) {
        mkdir($tempDir, 0777, true);
    }

        // Open and read the CSV file
        if (($handle = fopen($csvFile, "r")) !== FALSE) {
            // Skip the first row (header)
            fgetcsv($handle);

            while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
                // Assuming your CSV columns are in order (column1, column2, column3)
                $column1 = $data[0];
                $column2 = $data[1];
                $column3 = $data[2];

                // Insert data into the MySQL database
                $sql = "INSERT INTO import_csv_data (id, name, email) VALUES ('$column1', '$column2', '$column3')";
                mysqli_query($conn, $sql);

                // Generate a PDF for this record
                $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
                $pdf->AddPage();
                $pdf->SetFont('helvetica', '', 12);
                $pdf->Cell(0, 10, "ID: $column1", 0, 1);
                $pdf->Cell(0, 10, "Naam: $column2", 0, 1);
                $pdf->Cell(0, 10, "Email: $column3", 0, 1);
                // Customize the PDF content as needed

                // Save the PDF in the temporary directory
                $pdfFileName = $tempDir . "record_$column1.pdf";
                $pdf->Output($pdfFileName, 'F');

                // Delete the record from the database
                $deleteSql = "DELETE FROM import_csv_data WHERE id = '$column1'";
                mysqli_query($conn, $deleteSql);

                // Update progress
                $processedRecords++;

                // Send progress to the client
                echo "Processed $processedRecords out of $totalRecords records.<br />n";

                // Flush the output buffer to send data immediately to the client
                ob_flush();
                flush();

                // Close the PDF document
                $pdf->Close();
            }
            fclose($handle);
        }

    // Close the MySQL connection
    mysqli_close($conn);

    // Output a JavaScript script to perform the redirection
    echo '<script>window.location.href = "download.php";</script>';

    //Prevent any further execution
    exit;
}
?>

download.php:

<?php
// Specify the directory containing your PDF files
$directory = '/var/www/html/insuromatic/temp';

// Define the name of the ZIP archive file
$zipFileName = 'pdf_archive.zip';

// Create a ZipArchive object
$zip = new ZipArchive();

// Open the ZIP archive for writing
if ($zip->open($zipFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) {
    // Create a recursive directory iterator to scan the directory
    $iterator = new RecursiveDirectoryIterator($directory);
    $files = new RecursiveIteratorIterator($iterator);

    // Loop through all files in the directory
    foreach ($files as $file) {
        // Check if the file is a PDF
        if ($file->isFile() && strtolower(pathinfo($file, PATHINFO_EXTENSION)) === 'pdf') {
            // Add the PDF file to the ZIP archive with its original name
            $zip->addFile($file, $file->getBasename());
        }
    }

    // Close the ZIP archive
    $zip->close();

    // Remove temporary PDF files
    $tempDir = '/var/www/html/insuromatic/temp/';
    array_map('unlink', glob($tempDir . '*.pdf'));
    rmdir($tempDir);

    // Set the appropriate headers for a ZIP file download
    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="' . $zipFileName . '"');
    header('Content-Length: ' . filesize($zipFileName));

    // Send the ZIP file to the client's browser
    readfile($zipFileName);

    // Delete the ZIP file from the server (optional)
    unlink($zipFileName);

    exit; // Terminate the script

} else {
    echo "Failed to create ZIP archive.";
}
?>

2

Answers


  1. Chosen as BEST ANSWER

    The topic that @Progman referred to pointed me in the right direction. I reworked the code as follow:

    index.html:

    <!DOCTYPE html>
    <html>
    <head>
        <title>CSV to PDF</title>
    </head>
    <body>
    
        <div id="downloadlink">
            <a href="insuromatic.csv">Download demo CSV bestand</a>
        </div>
    
    
        <div id="generatePDFS"></div>
    
        <input type="file" id="csvFileInput" name="csvFileInput"><br />
        <button id="generatePDFSbutton">Genereer en download PDF bestanden</button><br />
    
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script>
        $(document).ready(function() {
            $("#generatePDFSbutton").click(function() {
                // Get the selected CSV file
                var csvFile = $("#csvFileInput")[0].files[0];
    
                if (csvFile) {
                    // Create a FormData object to send the file data
                    var formData = new FormData();
                    formData.append('action', 'generatePDFS');
                    formData.append('csvFile', csvFile);
    
                    $.ajax({
                        url: 'generatepdf.php',
                        type: 'POST',
                        data: formData,
                        processData: false,
                        contentType: false,
                        success: function(response) {
                            $("#generatePDFS").html(response);
                                fetch('download.php', {
                                    method: 'GET',
                                    responseType: 'blob',
                                })
    
                            .then(response => response.blob())
    
                            .then(blob => {
                                const url = window.URL.createObjectURL(blob);
                                const a = document.createElement('a');
                                a.style.display = 'none';
                                a.href = url;
                                a.download = 'pdf.zip';
    
                                document.body.appendChild(a);
                                a.click();
    
                                window.URL.revokeObjectURL(url);
                            })
    
                            .catch(error => {
                                console.error('Error downloading file:', error);
                            });
                        }
                    });
    
                } else {
                    alert('Please select a CSV file');
                }
            });
        });
    </script>
    </body>
    </html>
    
    

    generatepdf.php:

    <?php
    if (isset($_POST['action'])) {
        $action = $_POST['action'];
        if ($action === 'generatePDFS') {
            // Call PHP function generatePDFS
            $result = generatePDFS();
            echo $result;
        }
    }
    
        function generatePDFS() {
    
        // Include necessary libraries for database, PDF generation, and ZIP compression.
        require_once('db_connection.php');
        require_once('tcpdf/tcpdf.php');
    
        if (isset($_POST['action'])) {
    
            // Get uploaded file data
            $csvFile = $_FILES['csvFile']['tmp_name'];
    
            // Initialize progress variables
            $totalRecords = count(file($csvFile)) - 1; // Subtract 1 for the header row
            $processedRecords = 0;
    
            // Create a temporary directory to store the PDF files
            $tempDir = '/var/www/html/testomatic/temp/';
            if (!file_exists($tempDir)) {
                mkdir($tempDir, 0777, true);
            }
    
            // Open and read the CSV file
            if (($handle = fopen($csvFile, "r")) !== FALSE) {
                // Skip the first row (header)
                fgetcsv($handle);
    
                while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
                    // Assuming CSV columns are in order (column1, column2, column3)
                    $column1 = $data[0];
                    $column2 = $data[1];
                    $column3 = $data[2];
    
                    // Insert data into the MySQL database
                    $sql = "INSERT INTO import_csv_data (id, name, email) VALUES ('$column1', '$column2', '$column3')";
                    mysqli_query($conn, $sql);
    
                    // Generate a PDF for this record
                    $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
                    $pdf->AddPage();
                    $pdf->SetFont('helvetica', '', 12);
                    $pdf->Cell(0, 10, "ID: $column1", 0, 1);
                    $pdf->Cell(0, 10, "Naam: $column2", 0, 1);
                    $pdf->Cell(0, 10, "Email: $column3", 0, 1);
                    // Customize the PDF content as needed
    
                    // Save the PDF in the temporary directory
                    $pdfFileName = $tempDir . "record_$column1.pdf";
                    $pdf->Output($pdfFileName, 'F');
    
                    // Delete the record from the database
                    $deleteSql = "DELETE FROM import_csv_data WHERE id = '$column1'";
                    mysqli_query($conn, $deleteSql);
    
                    // Update progress
                    $processedRecords++;
    
                    // Send progress to the client
                    echo "$processedRecords van de $totalRecords records verwerkt.<br />n";
    
                    // Close the PDF document
                    $pdf->Close();
                }
                fclose($handle);
            }
    
        // Close the MySQL connection
        mysqli_close($conn);
    
        //Prevent any further execution
        exit;
        }
    }
    ?>
    

    The progress is displayed correctly and the download is offered. The issue is resolved.


  2. In order for

    ob_end_clean();
    

    to work you need to start output buffering first using

    ob_start();
    

    and must not use

    ob_flush();
    

    in between.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search