Sani Singer
Sani Singer

Reputation: 17

TYPO3V11 - fileReference: how to get files to create a ZIP

I’m relatively new to TYPO3 and currently managing a website built on this CMS. Since I have limited experience, I would greatly appreciate any assistance or guidance you can provide.

Thank you in advance!

I have array, use TYPO3-11.5.39, PHP-7.4.32.

<f:for each="{detail.images}" as="file">
<f:link.file file="{file.originalResource}" download="true" filename="{file.originalResource.properties.name}">Download file</f:link.file>                  
</f:for>

enter image description here enter image description here

I need to convert this array to another array to create an archive. Here is my controller. The function is tested, the archive is created with constant values. But I can't get files from TYPO3.

<?php

declare(strict_types=1);

namespace Vendor\extName\Controller;

use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Resource\FileReference;
use ZipArchive;



class DownloadController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
{
    /**
     * Download action to create and download a zip file of images
     *
     * @return void
     */


    public function downloadZipAction(): void
    {

       
        $files = [];

        foreach ($this->detail->images as $fileReference) {
            if ($fileReference instanceof \TYPO3\CMS\Extbase\Domain\Model\FileReference) {
                $file = $fileReference->getOriginalResource();

                if ($file instanceof \TYPO3\CMS\Core\Resource\FileReference) {
                    $identifier = $file->getIdentifier();
                    if ($identifier) {
                        $files[] = ['url' => $identifier];
                    }
                }
            }
        }




        $zipFileName = tempnam(sys_get_temp_dir(), 'images_') . '.zip';

        $zip = new ZipArchive();

        if ($zip->open($zipFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
            die('Could not create ZIP file.' );
        }

        foreach ($files as $file) {
            $fileContent = @file_get_contents($file); /
            if ($fileContent === false) {
                echo "Failed to fetch file:";
                continue; 
            }
            $fileName = basename($file);
            if (!$zip->addFromString($fileName, $fileContent)) {
                echo "Failed to add file: $fileName";
            }
        }

        $zip->close();

        if (!file_exists($zipFileName) || filesize($zipFileName) == 0) {
            unlink($zipFileName); 
            die('Failed to create a non-empty ZIP file.  ' );
        }

        header('Content-Type: application/zip');
        header('Content-Disposition: attachment; filename="images.zip"');
        header('Content-Length: ' . filesize($zipFileName));

        readfile($zipFileName);

        unlink($zipFileName);
        exit;
    }

}

My Fluid Template

                <f:link.action action="downloadZip" controller="Download" arguments="{detail: detail}" target="_blank">
                    Download Images as ZIP
                </f:link.action>

The controller works, but I get an empty archive.

Upvotes: 0

Views: 108

Answers (1)

Garvin Hicking
Garvin Hicking

Reputation: 556

I think the issue was that you created an array with a "url" substructure, but when you iterated it, you did not utilize that. Also the file path was not using an absolute directory. Please see the updated code, I've placed "@NEW" annotation to see the difference to your implementation. Hope it helps!

<?php declare(strict_types=1);

namespace Vendor\extName\Controller;

use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
// @NEW: Import this for access to base directory
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Resource\FileReference;
use ZipArchive;

class DownloadController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
{
    /**
     * Download action to create and download a zip file of images
     *
     * @return void
     */
    public function downloadZipAction(): void
    {
        $files = [];

        foreach ($this->detail->images as $fileReference) {
            if ($fileReference instanceof \TYPO3\CMS\Extbase\Domain\Model\FileReference) {
                $file = $fileReference->getOriginalResource();

                if ($file instanceof \TYPO3\CMS\Core\Resource\FileReference) {
                    $identifier = $file->getIdentifier();
                    if ($identifier) {
                        // @NEW: No longer use 'url' subkey, and create a full path instread.
                            $files[] = Environment::getPublicPath() . $file->getOriginalFile()->getPublicUrl();
                    }
                }
            }
        }

        $zipFileName = tempnam(sys_get_temp_dir(), 'images_') . '.zip';
        $zip = new ZipArchive();

        if ($zip->open($zipFileName, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
            die('Could not create ZIP file.' );
        }

        foreach ($files as $file) {
            $fileContent = @file_get_contents($file);
            if ($fileContent === false) {
                echo "Failed to fetch file:";
                continue; 
            }
            $fileName = basename($file);
            if (!$zip->addFromString($fileName, $fileContent)) {
                echo "Failed to add file: $fileName";
            }
        }

        $zip->close();

        // @NEW: Split these checks, because 'unlink' on a non-existing otherwise
        if (!file_exists($zipFileName)) {
            die('Failed to create ZIP file.');
        }

        if (filesize($zipFileName) == 0) {
            unlink($zipFileName); 
            die('Failed to create a non-empty ZIP file.');
        }

        header('Content-Type: application/zip');
        header('Content-Disposition: attachment; filename="images.zip"');
        header('Content-Length: ' . filesize($zipFileName));

        readfile($zipFileName);
        unlink($zipFileName);

        exit;
    }
}

Upvotes: 1

Related Questions