如何在php中上传和解析CSV文件

71

我想使用 PHP 上传一个 CSV 文件,上传完成后,希望能够显示该文件的数据。请提供一个示例,演示如何完成这个任务。


9
在 PHP 手册中,处理文件上传 涉及到了如何接收和处理通过 HTML 表单上传的文件。这个功能需要在 PHP 的配置文件中启用。一旦启用,你可以使用 $_FILES 数组来访问上传的文件。该数组包含上传文件的属性,例如文件名、类型、大小和临时文件名。在处理上传文件之前,你需要验证文件类型和大小,并确保目标文件夹有写入权限。最后,你可以使用 PHP 内置的函数(例如 move_uploaded_file())将上传的文件保存到指定的位置。 - Pekka
6
fgetcsv() 是 PHP 手册中的一个函数,用于从文件中读取一行并解析为 CSV 格式。该函数返回一个数组,其中包含解析后的字段。如果没有更多行可供读取,则返回 false。 - Pekka
3
你没有找到任何关于使用PHP上传文件的教程吗?我敢打赌一定有这样的教程存在... - Felix Kling
1
我很困惑该选择哪个插件,能否向我推荐一个最好的用于在PHP中上传文件的插件? - n92
PHP中没有“插件”,最多只有扩展,而且你不需要任何扩展来处理上传。 - Capsule
9个回答

172

现在可以用更简单的方式来完成这个任务。

$tmpName = $_FILES['csv']['tmp_name'];
$csvAsArray = array_map('str_getcsv', file($tmpName));

这将为您返回一个解析后的CSV数据数组。然后您可以使用foreach语句循环遍历它。


2
除了这个出色的解决方案之外,使用 explode(<delimiter>, $csv) 您可以分离每个字段,并在 CSV 数组的每个元素内拥有一个字段数组。 - Villapalos
2
$csvAsArray = array_map('str_getcsv', file($_FILES['csv']['tmp_name'])); 如果需要更简单的话。 - dvaey
1
虽然这似乎是快速简单的解决方案,但它并不完整或安全。它至少应该涉及move_uploaded_file()函数,以确保文件实际上是通过$_POST上传的;其次,没有检查是否为CSV文件。尝试上传一些图形文件,看看$csvAsArray里面有什么...! - Coder
非常出色的答案。 - Sarower Jahan
我有一个问题。如果我使用通常的fopen打开,我的文本会像这样["19","This is a \nlonger\nscrolling name","NULL"]。如果我使用上面的方法,它会变成["19","This is a \n"],["longer"],["scrolling name"","NULL"]。还有其他人遇到过类似的问题吗? - phantomhive

75

未经测试但应该能够给您一个想法。视图代码:

<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="csv" value="" />
<input type="submit" name="submit" value="Save" /></form>

上传文件的 upload.php 控制器:


$csv = array();

// check there are no errors
if($_FILES['csv']['error'] == 0){
    $name = $_FILES['csv']['name'];
    $ext = strtolower(end(explode('.', $_FILES['csv']['name'])));
    $type = $_FILES['csv']['type'];
    $tmpName = $_FILES['csv']['tmp_name'];

    // check the file is a csv
    if($ext === 'csv'){
        if(($handle = fopen($tmpName, 'r')) !== FALSE) {
            // necessary if a large csv file
            set_time_limit(0);

            $row = 0;

            while(($data = fgetcsv($handle, 1000, ',')) !== FALSE) {
                // number of fields in the csv
                $col_count = count($data);

                // get the values from the csv
                $csv[$row]['col1'] = $data[0];
                $csv[$row]['col2'] = $data[1];

                // inc the row
                $row++;
            }
            fclose($handle);
        }
    }
}

快速问题,解析后文件将被删除,对吗?它只是暂存于临时文件夹下,我说得对吗? - Bill
1
这是一个不错的解决方案,但我不确定为什么你只获取了每行的前两列?另外,$csv[$row]['row1'] = $data[0]; 这个命名有误导性,应该改成 $csv[$row]['column1'] = $data[0];。无论如何,更好的解决方案是使用 $csv[$row]=$data,并将整个文件制作成多维数组。 - kmoney12
1
最好检查 MIME 类型而不是文件扩展名。 - Raptor
1
感谢这个解决方案。我将$num编辑为$col_count,将row1、row2索引编辑为col1、col2,因为它们就是这样,而且这是唯一令人困惑的部分。我通常不会禁用执行时间,而是在while循环内设置set_time_limit(60),这样只要我们读取行,它就会保持重置状态,然后在完成后开始倒计时。这样,如果在退出循环后出现其他问题,它将像应该超时,我们就不必担心文件有多长了。 - Syntax Error
根据我的理解,它可能会被未来的保存所删除或覆盖,但这将是这些交互的副产品。因此,除非您明确声明,否则不要指望它被永久存储、删除或保存。 - Seraphendipity

52
尽管你可以很容易地找到一个关于如何使用php处理文件上传的教程,也有一些函数(手册)可用于处理CSV文件,但我会发布一些代码,因为就在几天前我曾经工作在一个项目上,其中包括一些你可以使用的代码...
HTML:
<table width="600">
<form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="post" enctype="multipart/form-data">

<tr>
<td width="20%">Select file</td>
<td width="80%"><input type="file" name="file" id="file" /></td>
</tr>

<tr>
<td>Submit</td>
<td><input type="submit" name="submit" /></td>
</tr>

</form>
</table>

PHP:

if ( isset($_POST["submit"]) ) {

   if ( isset($_FILES["file"])) {

            //if there was an error uploading the file
        if ($_FILES["file"]["error"] > 0) {
            echo "Return Code: " . $_FILES["file"]["error"] . "<br />";

        }
        else {
                 //Print file details
             echo "Upload: " . $_FILES["file"]["name"] . "<br />";
             echo "Type: " . $_FILES["file"]["type"] . "<br />";
             echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
             echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />";

                 //if file already exists
             if (file_exists("upload/" . $_FILES["file"]["name"])) {
            echo $_FILES["file"]["name"] . " already exists. ";
             }
             else {
                    //Store file in directory "upload" with the name of "uploaded_file.txt"
            $storagename = "uploaded_file.txt";
            move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $storagename);
            echo "Stored in: " . "upload/" . $_FILES["file"]["name"] . "<br />";
            }
        }
     } else {
             echo "No file selected <br />";
     }
}
我知道一定有更简单的方法,但我读取了CSV文件并将每条记录的单元格存储在一个二维数组中。
if ( isset($storagename) && $file = fopen( "upload/" . $storagename , r ) ) {

    echo "File opened.<br />";

    $firstline = fgets ($file, 4096 );
        //Gets the number of fields, in CSV-files the names of the fields are mostly given in the first line
    $num = strlen($firstline) - strlen(str_replace(";", "", $firstline));

        //save the different fields of the firstline in an array called fields
    $fields = array();
    $fields = explode( ";", $firstline, ($num+1) );

    $line = array();
    $i = 0;

        //CSV: one line is one record and the cells/fields are seperated by ";"
        //so $dsatz is an two dimensional array saving the records like this: $dsatz[number of record][number of cell]
    while ( $line[$i] = fgets ($file, 4096) ) {

        $dsatz[$i] = array();
        $dsatz[$i] = explode( ";", $line[$i], ($num+1) );

        $i++;
    }

        echo "<table>";
        echo "<tr>";
    for ( $k = 0; $k != ($num+1); $k++ ) {
        echo "<td>" . $fields[$k] . "</td>";
    }
        echo "</tr>";

    foreach ($dsatz as $key => $number) {
                //new table row for every record
        echo "<tr>";
        foreach ($number as $k => $content) {
                        //new table cell for every field of the record
            echo "<td>" . $content . "</td>";
        }
    }

    echo "</table>";
}

希望这能有所帮助,这只是一小段代码,我还没有测试过,因为我使用了稍微不同的方式。注释应该可以解释一切。


如果有人想要保存从内部“foreach”循环中获取的$content,该怎么办?我正在尝试在这里实现:http://stackoverflow.com/questions/14536221/how-to-pass-the-data-into-the-text-fields - Sumit Gera
2
下面使用 fgetcsv 的答案看起来更好,因为它应该正确处理转义字符。 - amh15
是的,我明白了,fgetcsv可能是更好的选择。 - David Stutz
1
@Mrworldwide PHP是一种服务器端程序,因此您必须上传文件或使用AJAX JS将数据发送回服务器。您可以在JS中读取而不保存(在这种情况下,请查看Papa Parse),但是这个问题明确要求PHP解决方案:“...在我上传文件之后...”。 - Seraphendipity
这太棒了!对于在WordPress中的使用,管理页面的完整URL在表单的“action”参数中是必要的。我在这里找到了一个不错的获取方式:https://www.geeksforgeeks.org/get-the-full-url-in-php/ - Chris Chalmers
显示剩余2条评论

4
我已经根据@Thomas Clowes的回答创建了以下代码示例。
$tmpName = $_FILES['csv']['tmp_name'];

$csv_data = array_map('str_getcsv', file($tmpName));

array_walk($csv_data , function(&$x) use ($csv_data) {
  $x = array_combine($csv_data[0], $x);
});

/** 
*
* array_shift = remove first value of array 
* in csv file header was the first value
* 
*/
array_shift($csv_data);

// Print Result Data
echo '<pre/>';
print_r($csv_data);    

它将会得到以下结果

 Array
(
   [0] => Array
    (
        
        [make] => Audi
        [model] => S4
        [grade] => 
        [chassis] => WAUZZZ8K9DA076529
        [year] => 2012
        [kms] => 127000
        [color] => White
        [doors] => 4
        [cc] => 3000
        [engineno] => 
        [trans] => FAT
        [fueltype] => P
        [condition] => 4            
        [price] => 15753
        
    )

)

1

您想查看PHP手册中的文件上传处理部分,同时建议您也查看fgetcsv()和explode()函数。


2
注意字符串值中的逗号、引号和换行符,应该使用状态机来分割CSV,而不是explode。 - Emyr

1

我认为 str_getcsv — 将 CSV 字符串解析为数组是您的最佳选择。

  1. 您需要将文件上传到服务器。

  2. 使用 str_getcsv 解析文件。

  3. 遍历数组并根据您网站的需求进行对齐。


谢谢您的回复。我还有一个问题想问您,哪个插件是最好的上传文件插件,可以避免代码大小,并且具有良好的用户界面,请为我推荐一个插件。 - n92
@vinay - 我更喜欢自己编写上传脚本。你所说的上传插件是什么意思?你可以自己编写一个...最多只需要10行代码。 - Hacker
@vinay - 如果你真的想要一个插件来进行(php + js)检查,请查看http://tinyurl.com/44nv6yt。 - Hacker

0
public function uploadCsvFile(){
   try{ 
    if($_SERVER['REQUEST_METHOD'] === 'POST'){
        $request = $this->post();
        $fileName = $request->file;
        $file = fopen($fileName, "r"); //open the file
        // $fileName = $_FILES["file"]["tmp_name"];
        while(($column = fgetcsv($file)) !== FALSE){
            $num = count($column);
            $this->dbw->query("insert into csv_upload(first_name,last_name,email,phone,address,pin) values('".$column[0]."','".$column[1]."','".$column[2]."','".$column[3]."','".$column[4]."','".$column[5]."')");
        }

        fclose($file);
    }
    }catch (Exception $e) {
        $this->responseErr($e->getMessage(), 500);
    }
}

2
请不要仅仅发布代码作为答案,还要提供解释您的代码是如何解决问题的。带有解释的答案通常更有帮助和更高质量,并且更有可能吸引赞同。 - Tyler2P

0
function doParseCSVFile($filesArray)
    {
        if ((file_exists($filesArray['frmUpload']['name'])) && (is_readable($filesArray['frmUpload']['name']))) { 

            $strFilePath = $filesArray['frmUpload']['tmp_name']; 

            $strFileHandle = fopen($strFilePath,"r");
            $line_of_text = fgetcsv($strFileHandle,1024,",","'"); 
            $line_of_text = fgetcsv($strFileHandle,1024,",","'"); 

            do { 
                if ($line_of_text[0]) { 
                    $strInsertSql = "INSERT INTO tbl_employee(employee_name, employee_code, employee_email, employee_designation, employee_number)VALUES('".addslashes($line_of_text[0])."', '".$line_of_text[1]."', '".addslashes($line_of_text[2])."', '".$line_of_text[3]."', '".$line_of_text[4]."')";
                    ExecuteQry($strInsertSql);
                }               
            } while (($line_of_text = fgetcsv($strFileHandle,1024,",","'"))!== FALSE);

        } else {
            return FALSE;
        }
    }

-2
你可以试试这个:
function doParseCSVFile($filesArray)
{
    if ((file_exists($filesArray['frmUpload']['name'])) && (is_readable($filesArray['frmUpload']['name']))) { 

        $strFilePath = $filesArray['frmUpload']['tmp_name']; 

        $strFileHandle = fopen($strFilePath,"r");
        $line_of_text = fgetcsv($strFileHandle,1024,",","'"); 
        $line_of_text = fgetcsv($strFileHandle,1024,",","'"); 

        do { 
            if ($line_of_text[0]) { 
                $strInsertSql = "INSERT INTO tbl_employee(employee_name, employee_code, employee_email, employee_designation, employee_number)VALUES('".addslashes($line_of_text[0])."', '".$line_of_text[1]."', '".addslashes($line_of_text[2])."', '".$line_of_text[3]."', '".$line_of_text[4]."')";
                ExecuteQry($strInsertSql);
            }               
        } while (($line_of_text = fgetcsv($strFileHandle,1024,",","'"))!== FALSE);

    } else {
        return FALSE;
    }
}

答案并不完全相关,而且是复制粘贴的东西。他在这里使用数据库等内容。 - Code Cooker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接