如何强制在WordPress后端中下载文件?


30

我想在我的一个WordPress插件中添加一个“单击下载”按钮,但不确定使用哪个钩子。到目前为止,将'admin_init'挂接到此代码似乎可行:

 header("Content-type: application/x-msdownload");
 header("Content-Disposition: attachment; filename=data.csv");
 header("Pragma: no-cache");
 header("Expires: 0");
 echo 'data';
 exit();

这似乎可行,但是我只想看看是否有最佳实践。

谢谢戴夫

Answers:


39

如果我对您的理解正确,那么您想要一个类似于以下内容URL,其对浏览器的响应将是您生成的内容,即您的.CSV文件,而不是WordPress生成的内容?

http://example.com/download/data.csv

我认为您正在寻找机会'template_redirect'。你可以找到'template_redirect'/wp-includes/template-loader.php这一个文件中的所有WordPress开发应该熟悉; 它简短而有趣,可以路由每个非管理员页面的加载,因此请务必仔细阅读。

只需将以下内容添加到主题functions.php文件或您所在的另一个文件includefunctions.php

add_action('template_redirect','yoursite_template_redirect');
function yoursite_template_redirect() {
  if ($_SERVER['REQUEST_URI']=='/downloads/data.csv') {
    header("Content-type: application/x-msdownload",true,200);
    header("Content-Disposition: attachment; filename=data.csv");
    header("Pragma: no-cache");
    header("Expires: 0");
    echo 'data';
    exit();
  }
}

注意:为测试'/downloads/data.csv'通过检查URL $_SERVER['REQUEST_URI']。还要注意添加,true,200到您的header()呼叫中的设置位置Content-type;这是因为WordPress 无法识别URL,因此将设置404 “未找到”状态代码。这虽然为没有问题的true通知header()更换404的WordPress已经建立并使用HTTP 200 “好”的状态代码,而不是。

这就是FireFox的样子请注意,屏幕截图没有/downloads/虚拟目录,因为在获取并注释了屏幕截图之后,添加'/downloads/'虚拟目录似乎是个好主意):

CSV文件的下载URL的屏幕截图
(来源:mikeschinkel.com

更新

如果希望从前缀为URL的URL处理下载,/wp-admin/以使用户直观地知道该文件受登录名的保护,则也可以这样做。一种方法的描述如下。

我封装成一类这个时候,叫DownloadCSV,并创建了一个用户“能力”'download_csv''administrator'角色(阅读关于角色和功能在这里 你可以只搭载预定义的关闭'export',如果你的角色一样,如果是只搜索和替换'download_csv''export'并删除该register_activation_hook()调用和该activate()函数。顺便说一句,需要激活钩子是我将其移至插件而不是保留主题functions.php文件的原因之一。*

我还使用了“工具”菜单,并添加了“下载CSV”菜单选项并将其链接到该功能。add_submenu_page()'download_csv'

最后,我选择了'plugins_loaded'挂钩,因为它是我可以使用的最早的合适挂钩。您可以使用,'admin_init'但是该钩子要晚得多运行(第1130个钩子调用与第3个钩子调用),那么为什么要让WordPress做更多的一次性工作呢?(我使用了Instrument Hooks插件来确定使用哪个钩子。)

在钩子中,我/wp-admin/tools.php通过检查$pagenow变量来确保URL以开头,current_user_can('download_csv')然后验证是否通过,然后测试$_GET['download']是否包含data.csv;如果是,我们实际上运行与以前相同的代码。我还,true,200header()上一个示例的调用中删除了,因为WordPress知道这是一个很好的URL,因此尚未设置404状态。所以这是您的代码:

<?php
/*
Plugin Name: Download CSV
Author: Mike Schinkel
Author URI: http://mikeschinkel.com
 */
if (!class_exists('DownloadCSV')) {
  class DownloadCSV {
    static function on_load() {
      add_action('plugins_loaded',array(__CLASS__,'plugins_loaded'));
      add_action('admin_menu',array(__CLASS__,'admin_menu'));
      register_activation_hook(__FILE__,array(__CLASS__,'activate'));
    }
    static function activate() {
      $role = get_role('administrator');
      $role->add_cap('download_csv');
    }
    static function admin_menu() {
      add_submenu_page('tools.php',    // Parent Menu
        'Download CSV',                // Page Title
        'Download CSV',                // Menu Option Label
        'download_csv',                // Capability
        'tools.php?download=data.csv');// Option URL relative to /wp-admin/
    }
    static function plugins_loaded() {
      global $pagenow;
      if ($pagenow=='tools.php' && 
          current_user_can('download_csv') && 
          isset($_GET['download'])  && 
          $_GET['download']=='data.csv') {
        header("Content-type: application/x-msdownload");
        header("Content-Disposition: attachment; filename=data.csv");
        header("Pragma: no-cache");
        header("Expires: 0");
        echo 'data';
        exit();
      }
    }
  }
  DownloadCSV::on_load();
}

这是激活的插件的屏幕截图:( 来源:mikeschinkel.com显示激活的插件的插件页面的屏幕截图

最后是触发下载的屏幕截图:( 来源:mikeschinkel.com从WordPress管理员的“工具”菜单中的选项通过URL下载文件的屏幕截图


迈克,谢谢您的帮助。使用此功能的唯一问题是我希望从后端下载文件。看起来template_redirect在后端不起作用,并且如果我不应该使用admin_init,我想知道我应该使用什么。admin_init似乎现在对我有用,至少在短期内我可能会坚持使用它。这是一个小功能,只有少数人会使用。
戴夫·莫里斯

@Dave Morris-您可以定义“后端”的含义吗?您是在服务器上意思吗?如果是,则'template_redirect'肯定可以在服务器上运行。如果没有,我会很困惑。你能澄清问题吗?提前致谢。
MikeSchinkel 2010年

@Dave:如果您用“后端”来表示管理区域,则该区域仍然有效。下载URL开头为/downloads/data.csv,这是一个不存在的文件,因此WordPress的“前端”将处理此请求并最终到达template-redirect。您只需在管理区域中创建一个指向该前端URL的链接。(必须这样说,您不会免费获得管理员登录保护-任何知道URL的人都可以下载该文件,但是也许有一种简单的方法可以解决该问题?)
Jan Fabry 2010年

@Jan Fabry-啊,我现在明白了。通过“后端”,他从管理中的意思吧?他可以将函数current_user_can()与上面的代码一起使用,或采用其他方法。发表评论后,我将在答案中添加更新。
MikeSchinkel 2010年

是的,很抱歉,我没有收到来自此站点的电子邮件警报,因此可以解释我延迟答复的原因。当我说“后端”时,的确是指WordPress的管理区域。对于那个很抱歉。我将尝试使用template_redirect看看会发生什么。谢谢!〜戴夫
戴夫·莫里斯

3

另一个有用的插件,可导出为CSV。可能对某人有用

    <?php

class CSVExport
{
/**
* Constructor
*/
public function __construct()
{
if(isset($_GET['download_report']))
{
$csv = $this->generate_csv();

header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"report.csv\";" );
header("Content-Transfer-Encoding: binary");

echo $csv;
exit;
}

// Add extra menu items for admins
add_action('admin_menu', array($this, 'admin_menu'));

// Create end-points
add_filter('query_vars', array($this, 'query_vars'));
add_action('parse_request', array($this, 'parse_request'));
}

/**
* Add extra menu items for admins
*/
public function admin_menu()
{
add_menu_page('Download Report', 'Download Report', 'manage_options', 'download_report', array($this, 'download_report'));
}

/**
* Allow for custom query variables
*/
public function query_vars($query_vars)
{
$query_vars[] = 'download_report';
return $query_vars;
}

/**
* Parse the request
*/
public function parse_request(&$wp)
{
if(array_key_exists('download_report', $wp->query_vars))
{
$this->download_report();
exit;
}
}

/**
* Download report
*/
public function download_report()
{
echo '<div class="wrap">';
echo '<div id="icon-tools" class="icon32">
</div>';
echo '<h2>Download Report</h2>';
//$url = site_url();

echo '<p>Export the Users';
}

/**
* Converting data to CSV
*/
public function generate_csv()
{
$csv_output = '';
$table = 'users';

$result = mysql_query("SHOW COLUMNS FROM ".$table."");

$i = 0;
if (mysql_num_rows($result) > 0) {
while ($row = mysql_fetch_assoc($result)) {
$csv_output = $csv_output . $row['Field'].",";
$i++;
}
}
$csv_output .= "\n";

$values = mysql_query("SELECT * FROM ".$table."");
while ($rowr = mysql_fetch_row($values)) {
for ($j=0;$j<$i;$j++) {
$csv_output .= $rowr[$j].",";
}
$csv_output .= "\n";
}

return $csv_output;
}
}

// Instantiate a singleton of this plugin
$csvExport = new CSVExport();

2

admin_init Hook或load-(page) Hook似乎有效,WordPress尚未在此状态下设置标头。我使用load-(page) Hook,因为它在加载管理菜单页面时运行。您可以为特定页面加载脚本。

您可以在WordPress Codex上检查load-(page)Hook

如果您使用的是admin_init Hook,请确保使用check_admin_referer或其他脚本来验证随机数,条件可能通过后会输出下载文件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.