【问题标题】:Download CSV file after click button单击按钮后下载 CSV 文件
【发布时间】:2018-04-06 07:24:53
【问题描述】:

大家早上好, 当用户单击“导出 CSV”按钮时,我需要强制下载 csv 文件,我看到很多页面(这里和谷歌)并尝试了很多东西,但任何东西都对我有用。 我有一个带有此按钮的主页,单击它会将 POST 请求(使用 Javascript 和 ajax)发送到制作 csv 文件并理论上下载文件的另一个页面。 这是主页的代码:

<html>
<head>
<script  type="text/javascript"  src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.4/Chart.min.js"></script>
</head>
<?php   
    $filter="<button id='exportCSV' >Export CSV</button><br/></div>";
    echo $filter 
?>
<script>

  $(function() {
      $("#exportCSV").click(function(){
          var query="select * from thcu_cabtemphpc order by timetag desc limit 300";
          var table="thcu_cabtemphpc";
          //alert(query+"  "+table);
          //alert(dataString);
          $.ajax({
              type: "POST",
              url: "exportCSV.php",
              data: {cvsExp: query, table:table},
              success: function(result){
                  alert("Export in progress");
                  //$("#export").html(result);

              }
          });
         });
      });
      </script>
</html>

当然这只是一个测试,我还有另一个主页,用户可以在其中选择查询和表格。 这是ExportCSV.php的代码

<html>
<body>
<?php

include('connection.php');
function array2csv($bd, $query, $id){
    $sql = mysql_query($query);
    $day = gmdate("d-M-Y");
    $offset   = +2 * 3600; // timezone offset for UTC-13
    $utcTime  = gmdate( 'd.m.Y H:i:s' );
    $milliseconds ="".round(microtime(true) * 1000);
    $val= substr($milliseconds,8,10);
    //echo $val;
    $valor = gmdate( 'd-m-Y_H-i-s-'.$val, time() + $offset );
    $name= $day."_".$id."data_export_".$valor.".csv";


    $fp = fopen($name, 'w');

    while ($res=mysql_fetch_row($sql)) {
        $count = 0;
        foreach ($res as $val) {
            if ($count == 0){
                $data = gmdate("Y-m-d H:i:s", substr(($val/10000000) - 12219292800, 0,10));
                $arr[$count] = $data;
                $count++;
            } else {
                $arr[$count] = $val;
            }

        }
        $delimiter=",";

        fputcsv($fp,$arr,$delimiter);
    }
    //fpassthru($fp);
    fclose($fp);        
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $name . '.csv"');
    header('Content-Length: ' . filesize($name)); 
    echo readfile($name);

}

    if(isset($_POST["cvsExp"])) {
        $query=$_POST["cvsExp"];
        $id=$_POST["table"];
        //download_send_headers("data_export_".gmdate("Y-m-d").".csv");
        array2csv($bd,$query,$id);
        die();
    }
?></body>
</html>

我尝试这样的代码但不起作用,我尝试用 text/csv 替换 application/octet,我尝试插入 echo readfile($name) 或仅 readfile($name) 但任何工作。这样做的结果是在 webroot 中创建了一个文件,这并不好,我希望浏览器下载该文件。 非常感谢。

编辑:我尝试仅使用下载脚本执行页面并且它可以工作,问题是当我使用 ajax 调用它时!!!怎么可能?哪里出错了?

【问题讨论】:

  • 将查询字符串从客户端发送到服务器端是非常糟糕且不安全的设计
  • 尝试返回readfile($name),而不是回显它。也使用exit() 而不是die()。看看:php.net/manual/en/function.readfile.php
  • 我尝试但没有任何改变。这只是一个测试,“真实”的页面是不同的。

标签: javascript php jquery csv


【解决方案1】:

我没有检查你的内部代码,但是CSV肯定不能以&lt;html&gt;标签开头!您只需回复 CSV 内容,删除除&lt;php&gt; 之外的所有标签。

例如:

<?php

include('connection.php');
function array2csv($bd, $query, $id){
    $sql = mysql_query($query);
    $day = gmdate("d-M-Y");
    $offset   = +2 * 3600; // timezone offset for UTC-13
    $utcTime  = gmdate( 'd.m.Y H:i:s' );
    $milliseconds ="".round(microtime(true) * 1000);
    $val= substr($milliseconds,8,10);
    //echo $val;
    $valor = gmdate( 'd-m-Y_H-i-s-'.$val, time() + $offset );
    $name= $day."_".$id."data_export_".$valor.".csv";


    $fp = fopen($name, 'w');

    while ($res=mysql_fetch_row($sql)) {
        $count = 0;
        foreach ($res as $val) {
            if ($count == 0){
                $data = gmdate("Y-m-d H:i:s", substr(($val/10000000) - 12219292800, 0,10));
                $arr[$count] = $data;
                $count++;
            } else {
                $arr[$count] = $val;
            }

        }
        $delimiter=",";

        fputcsv($fp,$arr,$delimiter);
    }
    //fpassthru($fp);
    fclose($fp);        
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $name . '.csv"');
    header('Content-Length: ' . filesize($name)); 
    echo readfile($name);

}

    if(isset($_POST["cvsExp"])) {
        $query=$_POST["cvsExp"];
        $id=$_POST["table"];
        //download_send_headers("data_export_".gmdate("Y-m-d").".csv");
        array2csv($bd,$query,$id);
        die();
    }
?>

我刚刚删除了标题和尾随 &lt;html&gt;&lt;body&gt; 标签

【讨论】:

    【解决方案2】:

    HTML 标签

    您指定您需要一种方法来强制下载(并且没有提供错误消息),所以我假设您可以通过浏览器正常访问它。

    您需要首先删除该页面上的所有 HTML 标记。当您echo readfile($name); 时,您要下载的结果文件将如下所示:

    <html>
    <head>
    <script  type="text/javascript"  src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.4/Chart.min.js"></script>
    </head>
    
    (YOUR CSV FILE HERE)
    
    </body>
    </html>
    

    标题函数

    所使用的任何 header() 函数都应在页面中的任何其他内容之前发送给客户端;它应该在您的代码的最顶部,甚至在您的 include connection.php; 行之前。

    【讨论】:

    • 好的,我用标题创建了一个新函数,我先调用它,但仍然不起作用。我用 exit() 更改 die() 并删除 ech 仍然一样。
    • 能否请您在您的header() 函数上方添加此代码:ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL);,刷新页面并回复它所说的内容?该代码将告诉 PHP 输出它遇到的任何错误。
    • 你把echo readfile($name);改成readfile($name);了吗?
    【解决方案3】:

    看看这是我动态下载 csv 的简单代码。

    if(isset($_POST['somebutton']))
    {
     $filename = "somefilename.csv";
     $fp = fopen('php://output', 'w');
    
     $query = "select * from thcu_cabtemphpc order by timetag desc limit 300";    
     $result = mysql_query($query);
     for($i=0;$i<=7;$i++)
     {
        $header[] = mysql_field_name($result, $i);
     }
    
     header('Content-type: application/csv');
     header('Content-Disposition: attachment; filename='.$filename);
     fputcsv($fp, $header);
    
     $query = "select * from thcu_cabtemphpc order by timetag desc limit 300";    
     $result12= mysql_query($query);
    
     while($row12 = mysql_fetch_row($result12)) {
      fputcsv($fp, $row12);
     }
    exit;
    }
    

    这是我下载简单 Excel 的代码。它的工作非常好。把这个 php 放在任何地方,你就可以下载 csv。

    【讨论】:

    • 同样的事情,不起作用!我真的不明白问题出在哪里!
    【解决方案4】:

    阿贾克斯

    您无法使用 Ajax (see here) 下载文件。

    相反,使用附加参数将其设为GET(或POST,如果您愿意)对exportCSV.php 文件的请求,即:

    <a href="exportCSV.php?table=table&query=name_of_the_query">Export CSV</a>
    

    由于是文件下载,它不会将您重定向到另一个页面,它只会开始下载文件。

    标题

    如果你把php和html结合起来,总是把逻辑和所有的处理放在文件的开头,所以:

    <?php
      header('Content-Type: application/octet-stream');
    ?>
    <html>
    </html>
    

    这将防止向浏览器发送错误的标头。 header() 函数只有在没有发送任何其他标头时才有效。

    请记住,header() 必须在发送任何实际输出之前调用,无论是通过普通 HTML 标记、文件中的空白行还是通过 PHP。 (source)

    功能

    我需要提一下,您应该绝不使用POST/GET 参数来发送SQL 查询!而是发送一些信息,例如查询名称和查询参数,然后您可以使用这些信息正确、安全地构建 SQL 查询,即:

    function array2scv($queryName, $queryParameters) {
      $queries = [
        'example' = 'SELECT * FROM user WHERE id = ?',
      ];
      $stmt = $mysqli->prepare($queries[$queryName]);
      $stmp->bind_param('i',  $queryParameters['id']);
      $stmp->execute();
      ...
    }
    

    然后return readfile($name) 在该函数中而不是回显它。和exit() 而不是die()。 (source)

    示例

    这是一个简单脚本的工作示例:

    <?php
    // example csv
    $url = 'http://samplecsvs.s3.amazonaws.com/Sacramentorealestatetransactions.csv';
    function download_file()
    {
        // getting content to use in the example
        $content = file_get_contents($url);
        $filename = '/tmp/sample.csv';
    
        // creating temp file
        $handle = fopen($filename, 'w+');
        fwrite($handle, $content);
        fclose($handle);
    
        // setting headers
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="'.basename($filename).'"');
        header('Content-Length: ' . filesize($filename));
        return readfile($filename);
    }
    
    if (isset($_GET['export']) && $_GET['export'] === '1') {
        download_file();
        exit();
    }
    
    ?>
    <html>
    <body>
    <a href="?export=1">Export</a>
        </body>
    </html>
    

    我希望这会有所帮助。

    【讨论】:

    • 您好,感谢您的帮助,此页面只是为了简化情况的测试。在“真实”页面中,我只发送信息,我尝试了您的建议,但仍然无效。
    • 很抱歉听到这个消息......我已经更新了我的答案,看看你是否可以修复你的代码。另外,我在您的代码中发现了另一个错误:$name 中已经有 .csv,但是当您设置 Content-disposition 标头时,您在末尾再次添加 csv。
    【解决方案5】:

    首先我要感谢大家的很多回答,今天我找到了一个解决方案,对我来说它有效,我在主页中添加了这个:

    $(function() {
            $('#CSV').click(function(){
                $('#wait-animation').show();
                var where="<?php Print($where) ?>";
                var table="<?php Print($id) ?>";
                //var table="thcu_cabtemphpc";
                alert("Export in progress");
                alert(query);
                document.location.href = 'exportCSV.php?where='+where+'&table='+table;
                $('#wait-animation').hide();
          });
      });
    

    在导出页面中:

    <?php
    include('connection.php');
    header("Last-Modified: " . @gmdate("D, d M Y H:i:s",$_GET['timestamp']) . " GMT");
    header("Content-type: text/x-csv");
    // If the file is NOT requested via AJAX, force-download
    if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') {
        header("Content-Disposition: attachment; filename=search_results.csv");
    }
    $table=$_GET['table'];
    $where=$_GET['where'];
    $day = gmdate("d-M-Y");
    $offset   = +2 * 3600; // timezone offset for UTC-13
    $utcTime  = gmdate( 'd.m.Y H:i:s' );
    $milliseconds ="".round(microtime(true) * 1000);
    $val= substr($milliseconds,8,10);
    $valor = gmdate( 'd-m-Y_H-i-s-'.$val, time() + $offset );
    $filename= $day."_".$table."data_export_".$valor.".csv";
    
    $fp = fopen('php://output', 'w');
    
    $query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='monitoring' AND TABLE_NAME='".$table."'";
    $result = mysql_query($query);
    while ($row = mysql_fetch_row($result)) {
        $header[] = $row[0];
    }   
    
    header('Content-type: application/csv');
    header('Content-Disposition: attachment; filename='.$filename);
    fputcsv($fp, $header);
    
    
    $result = mysql_query("select * from ".$id." where ".$where);
    while($row = mysql_fetch_row($result)) {
        fputcsv($fp, $row);
    }
    return $filename;
    exit;
    ?>
    

    非常感谢。 祝你有美好的一天。

    【讨论】:

      猜你喜欢
      • 2019-04-29
      • 2015-07-13
      • 2014-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-16
      • 2011-02-07
      相关资源
      最近更新 更多