PHP echo() 损坏 readfile() 输出

PHP echo() corrupting readfile() output

提问人:Willem-Jan Meijer 提问时间:10/3/2023 最后编辑:Willem-Jan Meijer 更新时间:10/3/2023 访问量:80

问:

我有这个PHP脚本(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);
}
?>

服务器配置:

Apache/2.4.57 (德贝安) PHP 8.0.30 FPM/快速CGI

问题:

  • 当第 65 行的 readfile() 语句被注释掉时,第 89 行的 echo() 语句工作正常,我看到屏幕上回显的进度
  • 当第 65 行的 echo() 语句被注释掉时,第 89 行的 readfile() 语句工作正常,我得到一个包含创建的 PDF 文件的 ZIP 下载文件

但是,当启用这两条行时,在进度回显到屏幕上后,我在屏幕上看到很多垃圾(我认为是原始 PDF 内容)。ZIP不提供下载。

不幸的是,我不知道我做错了什么。我尝试了 ob_end_clean()、ob_end_flush() 等几个地方,但没有运气,垃圾一直在打印。有人能给我指出正确的方向吗?

编辑:重做版本:

<?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.";
}
?>
php echo 读取文件

评论

2赞 Barmar 10/3/2023
您不能将 zip 数据与纯文本混合使用。脚本输出的所有内容都将包含在下载中。
0赞 Progman 10/3/2023
这回答了你的问题吗?PHP-如何解决Html页面源被附加到下载文件中的问题
0赞 knittl 10/3/2023
“下载”只是服务器“回显”的文件内容。如果您回显其他任何内容,则您不再拥有文件的内容,而是内容加上其他内容
0赞 Willem-Jan Meijer 10/3/2023
我试着理解...我预计 ob_end_clean();就在 readfile() 可以解决这个问题之前,但事实并非如此?
0赞 axiac 10/3/2023
65号线是哪条线?还是89?

答:

0赞 volkerschulz 10/3/2023 #1

为了

ob_end_clean();

要工作,您需要首先使用

ob_start();

并且不得使用

ob_flush();

介于两者之间。

0赞 Willem-Jan Meijer 10/3/2023 #2

@Progman提到的话题为我指明了正确的方向。我重新编写了代码如下:

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;
    }
}
?>

进度将正确显示并提供下载。问题已解决。