Как можно принудительно скачать файл в панели администратора WordPress?

31 окт. 2010 г., 18:29:28
Просмотры: 59.3K
Голосов: 34

Я хотел бы добавить кнопку "Нажмите для скачивания" в один из моих плагинов WordPress, и я не уверен, какой хук использовать. На данный момент, подключение к 'admin_init' с этим кодом, похоже, работает:

// Установка типа содержимого как скачиваемый файл
header("Content-type: application/x-msdownload");
// Установка заголовка для скачивания с именем файла data.csv
header("Content-Disposition: attachment; filename=data.csv");
// Отключение кэширования
header("Pragma: no-cache");
// Установка времени истечения
header("Expires: 0");
// Вывод данных
echo 'данные';
// Завершение выполнения скрипта
exit();

Это вроде бы работает, но я хотел бы узнать, есть ли какие-то лучшие практики для этого.

Спасибо, Dave

0
Все ответы на вопрос 3
8
46

Если я правильно вас понял, вы хотите иметь URL примерно следующего вида, ответом на который в браузере будет сгенерированное вами содержимое, то есть ваш файл .CSV, без какого-либо сгенерированного контента от WordPress?

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

Я думаю, вам нужен хук 'template_redirect'. Вы можете найти 'template_redirect' в файле /wp-includes/template-loader.php, с которым должны быть знакомы все разработчики WordPress; он короткий и простой, а также обрабатывает каждый запрос неадминистративной страницы, поэтому обязательно изучите его.

Просто добавьте следующее в файл functions.php вашей темы или в другой файл, который вы подключаете через include в functions.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();
  }
}

Обратите внимание на проверку URL '/downloads/data.csv' через $_SERVER['REQUEST_URI']. Также обратите внимание на добавленные параметры ,true,200 в вызов header() при установке Content-type; это связано с тем, что WordPress уже установил код состояния 404 "Не найдено", так как не распознал URL. Однако это не проблема, так как параметр true указывает header() заменить 404, установленный WordPress, на HTTP-код 200 "OK".

Вот как это выглядит в FireFox (Примечание: на скриншоте нет виртуальной директории /downloads/, потому что после создания и аннотирования скриншота показалось хорошей идеей добавить виртуальную директорию '/downloads/'):

Скриншот URL для загрузки CSV-файла
(источник: mikeschinkel.com)

ОБНОВЛЕНИЕ

Если вы хотите, чтобы загрузка происходила по 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, чтобы определить, какой хук использовать.)

В хуке я проверяю, начинается ли URL с /wp-admin/tools.php, анализируя переменную $pagenow, убеждаюсь, что current_user_can('download_csv'), и если все в порядке, проверяю $_GET['download'] на наличие значения data.csv; если да, запускается практически тот же код, что и раньше. Я также убрал параметры ,true,200 из вызова header() в предыдущем примере, так как здесь 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',    // Родительское меню
        'Загрузить CSV',              // Заголовок страницы
        'Загрузить CSV',              // Название пункта меню
        'download_csv',               // Возможность
        'tools.php?download=data.csv');// URL относительно /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)

Наконец, вот скриншот запуска загрузки: Скриншот загрузки файла по URL из пункта меню Инструменты админ-панели WordPress
(источник: mikeschinkel.com)

1 нояб. 2010 г. 13:09:54
Комментарии

Майк, спасибо за помощь. Единственный нюанс с этой функцией в том, что я хочу, чтобы файл скачивался из бэкенда. Похоже, что template_redirect не работает в бэкенде, и если мне не следует использовать admin_init, интересно, что использовать вместо него. admin_init вроде бы работает для меня сейчас, возможно, я оставлю его, по крайней мере, на короткий срок. Это второстепенная функция, которой будут пользоваться только несколько человек.

Dave Morris Dave Morris
3 нояб. 2010 г. 04:37:54

@Dave Morris - Можете уточнить, что вы подразумеваете под "бэкендом"? Вы имеете в виду на сервере? Если да, то 'template_redirect' определенно выполняется на сервере. Если нет, я совсем запутался; не могли бы вы прояснить вопрос? Заранее спасибо.

MikeSchinkel MikeSchinkel
3 нояб. 2010 г. 14:00:24

@Dave: Если под "бэкендом" вы имеете в виду админку, это все равно будет работать. URL для скачивания начинается с /downloads/data.csv, что является несуществующим файлом, поэтому WordPress "фронтенд" обработает этот запрос и в итоге достигнет template-redirect. Вы просто создаете ссылку в админке, которая ведет на этот фронтенд URL. (Стоит отметить, что таким образом вы не получаете защиту авторизации в админке бесплатно — любой, кто знает URL, сможет скачать файл, но возможно, есть простой способ это исправить?)

Jan Fabry Jan Fabry
3 нояб. 2010 г. 22:41:37

@Jan Fabry - А, теперь я понял. Под "бэкендом" он имел в виду админку, верно? Он может использовать функцию current_user_can() с приведённым выше кодом или выбрать другой подход. После этого комментария я обновлю свой ответ.

MikeSchinkel MikeSchinkel
4 нояб. 2010 г. 11:55:45

Да, извините, я не получаю email-уведомления с этого сайта, поэтому задержка с ответом. Я действительно имел в виду админку WordPress, когда говорил "бэкенд". Прошу прощения. Попробую использовать template_redirect и посмотрю, что получится. Спасибо! ~Dave

Dave Morris Dave Morris
6 нояб. 2010 г. 00:47:30

@Dave Morris - template_redirect больше подходит для внешнего доступа. Попробуй второй пример, который я привёл.

MikeSchinkel MikeSchinkel
6 нояб. 2010 г. 01:05:30

Это отлично. Вы избавили меня от множества проблем. Я просто хочу уточнить для других, кому может не понадобиться страница подменю, что всё, что нужно сделать — это подключить метод класса DownloadCSV admin_menu к хуку действия с вызовом add_action('admin_menu',array(__CLASS__,'admin_menu')); и добавить ваш код для принудительной загрузки в метод.

racl101 racl101
30 янв. 2014 г. 07:16:17

Если вы хотите использовать WP_Query для получения данных для создания скачиваемого файла, используйте хук 'admin_init' вместо 'plugins_loaded'. В противном случае вы получите "Fatal error: Call to a member function get_queried_object_id()".

Gustavo Daniel Gustavo Daniel
15 июл. 2016 г. 01:42:48
Показать остальные 3 комментариев
0

еще один полезный плагин для экспорта в CSV. может кому-то пригодиться

<?php

class CSVExport
{
    /**
    * Конструктор
    */
    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_action('admin_menu', array($this, 'admin_menu'));

        // Создаем конечные точки
        add_filter('query_vars', array($this, 'query_vars'));
        add_action('parse_request', array($this, 'parse_request'));
    }

    /**
    * Добавляем дополнительные пункты меню для администраторов
    */
    public function admin_menu()
    {
        add_menu_page('Скачать отчет', 'Скачать отчет', 'manage_options', 'download_report', array($this, 'download_report'));
    }

    /**
    * Разрешаем использование пользовательских переменных запроса
    */
    public function query_vars($query_vars)
    {
        $query_vars[] = 'download_report';
        return $query_vars;
    }

    /**
    * Обрабатываем запрос
    */
    public function parse_request(&$wp)
    {
        if(array_key_exists('download_report', $wp->query_vars))
        {
            $this->download_report();
            exit;
        }
    }

    /**
    * Скачивание отчета
    */
    public function download_report()
    {
        echo '<div class="wrap">';
        echo '<div id="icon-tools" class="icon32">
        </div>';
        echo '<h2>Скачать отчет</h2>';
        //$url = site_url();

        echo '<p>Экспорт пользователей';
    }

    /**
    * Конвертация данных в 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;
    }
}

// Создаем экземпляр плагина
$csvExport = new CSVExport();
4 янв. 2013 г. 18:26:38
0

Хук admin_init или хук load-(page) кажется работают, WordPress в этом состоянии еще не установил заголовки. Я использую хук load-(page), потому что он срабатывает при загрузке страницы админ-меню. Вы можете загружать свой скрипт для конкретной страницы.

Вы можете проверить хук load-(page) в WordPress Codex

Если вы используете хук admin_init, убедитесь, что проверяете nonce с помощью check_admin_referer, иначе другие скрипты могут пройти условие и вывести ваш файл для скачивания.

24 дек. 2014 г. 09:54:38