SYMFONY2:使用StreamedResponse的csv导出不起作用

时间:2014-12-09 15:18:25

标签: php symfony csv doctrine-orm

我有一个问题,我的代码看起来很好我有一个状态200,正确的标题,但我创建的csv文件不会下载...

我没有错误,所以我不明白为什么......

如果你可以帮助我?

谢谢

这是我的代码:

    namespace Rac\CaraBundle\Manager;

/* Imports */

use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\ValidatorInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\HttpFoundation\StreamedResponse;

/* Local Imports */
use Rac\CaraBundle\Entity\Contact;

/**
 * Class CSV Contact Importer
 *
 */
class CSVContactImporterManager {

    /**
     * @var ObjectManager
     */
    private $om;

    /**
     * @var EventDispatcherInterface
     */
    private $eventDispatcher;

    /**
     * @var ValidatorInterface
     */
    private $validator;

    /**
     * @var ContactManager
     */
    private $contactManager;


    /**
     * @param EventDispatcherInterface $eventDispatcher
     * @param ObjectManager            $om
     * @param Contact                  $contactManager
     *
     */
    public function __construct(
    EventDispatcherInterface $eventDispatcher, ObjectManager $om, ValidatorInterface $validator, ContactManager $contactManager
    ) {
        $this->eventDispatcher = $eventDispatcher;
        $this->om = $om;
        $this->validator = $validator;
        $this->contactManager = $contactManager;
    }
    public function getExportToCSVResponse() {
        // get the service container to pass to the closure
        $contactList = $this->contactManager->findAll();
        $response = new StreamedResponse();
        $response->setCallback(
            function () use ($contactList) {
            //Import all contacts
            $handle = fopen('php://output', 'r+');
            // Add a row with the names of the columns for the CSV file
            fputcsv($handle, array('Nom', 'Prénom', 'Société', 'Position', 'Email', 'Adresse', 'Téléphone', 'Téléphone mobile'), "\t");
            $header = array();
            //print_r($contactList);
            foreach ($contactList as $row) {
                fputcsv($handle, array(
                    $row->getFirstName(),
                    $row->getLastName(),
                    $row->getCompany(),
                    $row->getPosition(),
                    $row->getEmail(),
                    $row->getAddress(),
                    $row->getPhone(),
                    $row->getMobile(),
                    ), "\t");
            }
            fclose($handle);
        }
        );
        $response->headers->set('Content-Type', 'application/force-download');
        $response->headers->set('Content-Disposition', 'attachment; filename="export.csv"');

        return $response;
    }

我的控制员:

    use Rac\CaraBundle\Entity\Contact;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    use Symfony\Component\HttpFoundation\Request;
    use UCS\Bundle\RichUIBundle\Controller\BaseController;
    use UCS\Bundle\RichUIBundle\Serializer\AbstractListSerializer;

    /**
     * Contact BackOffice Environment Controller.
     *
     *
     *
     * @Route("/contact_environment")
     */
    class ContactEnvironmentController extends BaseController{
        /* My code here..*/


       /**
         * @Route("/export", name="contact_environment_export",options={"expose"=true})
         * @Method("GET")
         *
         * @return type
         */
        public function exort(){
            $manager = $this->get("cara.csv_contact_importer_manager");
           return $manager->getExportToCSVResponse();



}
}

我的标题:

Cache-Control:no-cache, private
Connection:close
Content-Disposition:attachment; filename="export.csv"
Content-Type:application/force-download

And my previous question

4 个答案:

答案 0 :(得分:2)

以下是作者要求的基于响应的解决方案。在此设计中,csv服务仅返回csv文本。响应在控制器中生成。

csv生成器:

class ScheduleGameUtilDumpCSV
{
public function getFileExtension() { return 'csv'; }
public function getContentType()   { return 'text/csv'; }

public function dump($games)
{
    $fp = fopen('php://temp','r+');

    // Header
    $row = array(
        "Game","Date","DOW","Time","Venue","Field",
        "Group","HT Slot","AT Slot",
        "Home Team Name",'Away Team Name',
    );
    fputcsv($fp,$row);

    // Games is passed in
    foreach($games as $game)
    {
        // Date/Time
        $dt   = $game->getDtBeg();
        $dow  = $dt->format('D');
        $date = $dt->format('m/d/Y');
        $time = $dt->format('g:i A');

        // Build up row
        $row = array();
        $row[] = $game->getNum();
        $row[] = $date;
        $row[] = $dow;
        $row[] = $time;
        $row[] = $game->getVenueName();
        $row[] = $game->getFieldName();

        $row[] = $game->getGroupKey();

        $row[] = $game->getHomeTeam()->getGroupSlot();
        $row[] = $game->getAwayTeam()->getGroupSlot();
        $row[] = $game->getHomeTeam()->getName();
        $row[] = $game->getAwayTeam()->getName();

        fputcsv($fp,$row);
    }
    // Return the content
    rewind($fp);
    $csv = stream_get_contents($fp);
    fclose($fp);
    return $csv;
}

控制器:

public function renderResponse(Request $request)
{   
    // Model is passed via request
    $model = $request->attributes->get('model');
    $games = $model->loadGames();

    // The csv service
    $dumper = $this->get('csv_dumper_service');

    // Response with content
    $response = new Response($dumper->dump($games);

    // file prefix was injected
    $outFileName = $this->prefix . date('Ymd-Hi') . '.' . $dumper->getFileExtension();

    $response->headers->set('Content-Type', $dumper->getContentType());
    $response->headers->set('Content-Disposition', sprintf('attachment; filename="%s"',$outFileName));

    return $response;
}

答案 1 :(得分:1)

这是一个流媒体Symfony响应,工作正常。该类创建了一个要下载的文件,其中包含导出的数据。

class ExportManagerService {

    protected $filename;
    protected $repdata;


    public function publishToCSVReportData(){

        $repdata  = $this->repdata;
// array check
        if (is_array($repdata)){

            $response = new StreamedResponse();
            $response->setCallback(
                function () use ($repdata) {
                    $handle = fopen('php://output', 'r+');
                    foreach ($repdata as $row) {

                        $values = $row['values'];
                        $position = $row['position'];

                        $fileData = $this->structureDataInFile($values, $position);
                        fputcsv($handle, $fileData);
                    }
                    fclose($handle);
                }
            );
        } else{
            throw new Exception('The report data to be exported should be an array!');
        }

        $compstring = substr($this->filename,-4);
        if($compstring === '.csv'){
// csv file type check
            $response->headers->set('Content-Type', 'application/force-download');
            $response->headers->set('Content-Disposition', 'attachment; filename='.$this->filename);
        } else { throw new Exception('Incorrect file name!');}


        return $response;

    }

    public function structureDataInFile(array $values, $position){

        switch ($position){
            case 'TopMain':
                for ($i = 0; $i < 4; $i++){
                    array_unshift($values, ' ');
                }
                return $values;
                break;
            case 'Top':
                $space = array(' ', ' ', ' ');
                array_splice($values,1,0,$space);
                return $values;
                break;
            case 'TopFirst':
                for ($i = 0; $i < 1; $i++){
                    array_unshift($values, ' ');
                }
                $space = array(' ', ' ');
                array_splice($values,2,0,$space);
                return $values;
                break;
            case 'TopSecond':
                for ($i = 0; $i < 2; $i++){
                    array_unshift($values, ' ');
                }
                $space = array(' ');
                array_splice($values,3,0,$space);
                return $values;
                break;
            case 'TopThird':
                for ($i = 0; $i < 3; $i++){
                    array_unshift($values, ' ');
                }
                return $values;
                break;
            default:
                return $values;
        }
    }

    /*
    * @var array
    */
    public function setRepdata($repdata){
        $this->repdata = $repdata;
    }

    /*
    * @var string
    */
    public function setFilename($filename){
        $this->filename = $filename;
    }
}

答案 2 :(得分:1)

这是一种更短的方式:)

/**
 * Class CsvResponse
 */
class CsvResponse extends StreamedResponse
{
    /**
     * CsvResponse constructor.
     *
     * @param array  $rows
     * @param string $fileName
     */
    public function __construct(array $rows, $fileName)
    {
        parent::__construct(
            function () use ($rows) {
                $this->convertArrayToCsv($rows);
            },
            self::HTTP_OK,
            [
                'Content-Disposition' => sprintf('attachment; filename="%s"', $fileName),
                'Content-Type' => 'text/csv',
            ]
        );
    }

    /**
     * @param array $rows
     *
     */
    private function convertArrayToCsv(array $rows)
    {
        $tempFile = fopen('php://output', 'r+b');
        foreach ($rows as $row) {
            fputcsv($tempFile, $row);
        }
        fclose($tempFile);
    }
}

答案 3 :(得分:1)

这是我使用过不止一次的简单实现,实际上按照要求使用了 StreamRepsonse

这是一个新的响应类,它扩展了 StreamResponse 并具有类似的签名。还接受 $separator$enclosure 参数,例如需要使用分号 (;) 而不是逗号等。

如果需要创建更大的文件,它会在 php://temp 中创建 CSV 以尝试节省内存,并使用 stream_get_contents 一次检索一点。

class StreamedCsvResponse extends StreamedResponse
{
    private string $filename;

    public function __construct(
        private array $data,
        ?string $filename = null,
        private string $separator = ',',
        private string $enclosure = '"',
        $status = 200,
        $headers = []
    ) {
        if (null === $filename) {
            $filename = uniqid() . '.csv';
        }

        if (!str_ends_with($filename, '.csv')) {
            $filename .= '.csv';
        }

        $this->filename = $filename;
        
        parent::__construct([$this, 'stream'], $status, $headers);
        $this->setHeaders();
    }

    private function setHeaders(): void
    {
        $this->headers->set(
            'Content-disposition',
            HeaderUtils::makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $this->filename)
        );

        if (!$this->headers->has('Content-Type')) {
            $this->headers->set('Content-Type', 'text/csv; charset=UTF-8');
        }

        if (!$this->headers->has('Content-Encoding')) {
            $this->headers->set('Content-Encoding', 'UTF-8');
        }
    }

    public function stream(): void
    {
        $handle = fopen('php://temp', 'r+b');

        $this->encode($this->data, $handle);

        if (!is_resource($handle)) {
            return;
        }

        rewind($handle);

        while ($t = stream_get_contents($handle, 1024)) {
            echo $t;
        }

        fclose($handle);
    }

    private function encode(array $data, $handle): void
    {
        if (!is_resource($handle)) {
            return;
        }

        foreach ($data as $row) {
            fputcsv($handle, $row, $this->separator, $this->enclosure);
        }
    }
}