在 PHP 中强制下载文件 - 在 Joomla 框架内

2022-01-06 00:00:00 download php joomla

我有一些 PHP 代码可以在数据库上运行查询,将结果保存到 csv 文件,然后允许用户下载该文件.问题是,csv 文件包含围绕实际 csv 内容的页面 HTML.

I have some PHP code that runs a query on a database, saves the results to a csv file, and then allows the user to download the file. The problem is, the csv file contains page HTML around the actual csv content.

我已经在这里阅读了所有相关问题,包括这个.不幸的是,我的代码存在于 Joomla 中,所以即使我尝试重定向到一个只包含标题的页面,Joomla 也会自动用它自己的导航代码包围它.这仅在下载时发生;如果我查看保存在服务器上的 csv 文件,它不包含 HTML.

I've read all the related questions here already, including this one. Unfortunately my code exists within Joomla, so even if I try to redirect to a page that contains nothing but headers, Joomla automatically surrounds it with its own navigation code. This only happens at the time of download; if I look at the csv file that's saved on the server, it does not contain the HTML.

任何人都可以帮助我强制下载服务器上的实际 csv 文件,而不是浏览器正在编辑它的样子吗?我试过使用标题位置,如下所示:

Can anyone help me out with a way to force a download of the actual csv file as it is on the server, rather than as the browser is editing it to be? I've tried using the header location, like this:

header('Location: ' . $filename);

但它在浏览器中打开文件,而不是强制保存对话框.

but it opens the file in the browser, rather than forcing the save dialog.

这是我当前的代码:

//set dynamic filename
$filename = "customers.csv";
//open file to write csv
$fp = fopen($filename, 'w');

//get all data
$query = "select 
   c.firstname,c.lastname,c.email as customer_email, 
   a.email as address_email,c.phone as customer_phone,
   a.phone as address_phone,
   a.company,a.address1,a.address2,a.city,a.state,a.zip, c.last_signin
   from {$dbpre}customers c
   left  join  {$dbpre}customers_addresses a  on c.id = a.customer_id  order by c.last_signin desc";

$votes = mysql_query($query) or die ("File: " . __FILE__ . "<br />Line: " . __LINE__ . "<p>{$query}<p>" . mysql_error());
$counter = 1;
while ($row = mysql_fetch_array($votes,1)) {
    //put header row
    if ($counter == 1){
       $headerRow = array();
       foreach ($row as $key => $val)
          $headerRow[] = $key;
       fputcsv($fp, $headerRow);
    }
    //put data row
    fputcsv($fp, $row);
    $counter++;
}

//close file
fclose($fp);

//redirect to file
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=".$filename);
header("Content-Transfer-Encoding: binary");
readfile($filename); 
exit;

编辑完整网址如下所示:

http://mysite.com/administrator/index.php?option=com_eimcart&task=customers

实际下载链接如下所示:

with the actual download link looking like this:

http://mysite.com/administrator/index.php?option=com_eimcart&task=customers&subtask=export

更多编辑这是代码所在页面的截图;生成的文件仍在为子菜单拉入 html.所选链接的代码(导出为 CSV)现在是

MORE EDITS Here's a shot of the page that the code is on; the generated file still is pulling in the html for the submenu. The code for the selected link (Export as CSV) is now

index.php?option=com_eimcart&task=customers&subtask=export&format=raw

现在这是生成的保存文件的屏幕截图:

Now here is a screenshot of the generated, saved file:

这里在上传过程中缩小了,但是黄色突出显示的文本是子导航的 html 代码(列出客户,添加新客户,导出为 csv).这是我的完整代码现在的样子;如果我能去掉最后一点 html 就完美了.

It shrank during the upload here, but the text highlighted in yellow is the html code for the subnav (list customers, add new customer, export as csv). Here's what my complete code looks like now; if I could just get rid of that last bit of html it would be perfect.

  $fp= fopen("php://output", 'w');

            $query = "select c.firstname,c.lastname,c.email as customer_email, 
                      a.email as address_email,c.phone as customer_phone,
                      a.phone as address_phone,  a.company, a.address1,
                      a.address2,a.city,a.state,a.zip,c.last_signin

                      from {$dbpre}customers c
                      left  join  {$dbpre}customers_addresses a on c.id = a.customer_id  
                      order by c.last_signin desc";

            $votes = mysql_query($query) or die ("File: " . __FILE__ . "<br />Line: " . __LINE__ . "<p>{$query}<p>" . mysql_error());
            $counter = 1;

            //redirect to file
            header("Content-type: application/octet-stream");
            header("Content-Disposition: attachment; filename=customers.csv");
             header("Content-Transfer-Encoding: binary");

            while ($row = mysql_fetch_array($votes,1)) {

                    //put header row
                    if ($counter == 1){
                            $headerRow = array();
                            foreach ($row as $key => $val)
                                    $headerRow[] = $key;

                            fputcsv($fp, $headerRow);
                    }

                    //put data row
                    fputcsv($fp, $row);
                $counter++;
            }

            //close file
            fclose($fp);

BJORN 更新

这是对我有用的代码(我认为).在调用操作的链接中使用 RAW 参数:

Here's the code (I think) that worked for me. Use the RAW param in the link that calls the action:

index.php?option=com_eimcart&task=customers&subtask=export&format=raw

因为这是程序性的,我们的链接位于一个名为 customer.php 的文件中,如下所示:

Because this was procedural, our link was in a file called customers.php, which looks like this:

switch ($r['subtask']){
    case 'add':
    case 'edit':
        //if the form is submitted then go to validation
                include("subnav.php");
        if ($r['custFormSubmitted'] == "true")
            include("validate.php");
        else
            include("showForm.php");
        break;

    case 'delete':
                include("subnav.php");
        include("process.php");
            break;

    case 'resetpass':
                include("subnav.php");
        include("resetpassword");
            break;

    case 'export':
        include("export_csv.php");
            break;


    default:
                include("subnav.php");
        include("list.php");
        break;
}

因此,当用户单击上面的链接时,会自动包含 export_csv.php 文件.该文件包含所有实际代码:

So when a user clicked on the link above, the export_csv.php file is automatically included. That file contains all the actual code:

<?
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=customers.csv");
header("Content-Transfer-Encoding: binary");
$fp= fopen("php://output", 'w');


//get all data
$query = "select 
    c.firstname,c.lastname,c.email as customer_email, 
    a.email as address_email,c.phone as customer_phone,
    a.phone as address_phone,
    a.company,a.address1,a.address2,a.city,a.state,a.zip, c.last_signin

    from {$dbpre}customers c

    left  join  {$dbpre}customers_addresses a  on c.id = a.customer_id  order by c.last_signin desc";


$votes = mysql_query($query) or die ("File: " . __FILE__ . "<br />Line: " . __LINE__ . "<p>{$query}<p>" . mysql_error());
$counter = 1;

while ($row = mysql_fetch_array($votes,1)) {

    //put header row
    if ($counter == 1){
        $headerRow = array();
        foreach ($row as $key => $val)
            $headerRow[] = $key;

        fputcsv($fp, $headerRow);
    }

    //put data row
    fputcsv($fp, $row);
    $counter++;
}

//close file
fclose($fp);

推荐答案

这是我刚刚编写的一段示例代码,以帮助您解决问题.将其用作控制器中的操作方法.

This is a piece of sample code that I just cooked up to help you out. Use it as an action method in your controller.

function get_csv() {
        $file = JPATH_ADMINISTRATOR . DS . 'test.csv';

        // Test to ensure that the file exists.
        if(!file_exists($file)) die("I'm sorry, the file doesn't seem to exist.");

        // Send file headers
        header("Content-type: text/csv");
        header("Content-Disposition: attachment;filename=test.csv");

        // Send the file contents.
        readfile($file);
    }

仅此是不够的,因为您下载的文件仍将包含周围的 html.要摆脱它并只接收 csv 文件的内容,您需要将 format=raw 参数添加到您的请求中.在我的情况下,该方法位于 com_csvexample 组件内,因此网址为:

This alone will not be enough, because the file you download will still contain the surrounding html. To get rid of it and only receive the csv file's contents you need to add format=raw parameter to your request. In my case the method is inside the com_csvexample component, so the url would be:

/index.php?option=com_csvexample&task=get_csv&format=raw

编辑

为了避免使用中间文件替代

In order to avoid using an intermediate file substitute

//set dynamic filename
$filename = "customers.csv";
//open file to write csv
$fp = fopen($filename, 'w');

//open the output stream for writing
//this will allow using fputcsv later in the code
$fp= fopen("php://output", 'w');

使用此方法,您必须在将任何内容写入输出之前移动发送标头的代码.您也不需要调用 readfile 函数.

Using this method you have to move the code that sends headers before anything is written to the output. You also won't need the call to the readfile function.

相关文章