如何使用typo3/extbase触发下载?

发布于 2024-10-30 16:36:57 字数 294 浏览 0 评论 0 原文

我将 Typo3 与 extbase 和 Fluid 一起使用。我有一个控制器,其操作名为 downloadAction()。调用该操作后,系统尝试呈现下载模板(但我只想开始下载)。

public function downloadAction($id) {
  // create file
  // send header
  // dump file
  // exit
}

如何转储创建的下载文件并发送下载标头而不是正常的渲染过程?最好的方法是什么?

谢谢

I'm using Typo3 with extbase and fluid. I have a Controller with an action called downloadAction(). After calling the action the system tries to render the download-template (but I just want to start a download).

public function downloadAction($id) {
  // create file
  // send header
  // dump file
  // exit
}

How can I dump the created download-File and send a download header instead of the normal render process? What is the best way?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

橘寄 2024-11-06 16:36:57

几个月前我在一个项目中做到了这一点,它非常简单。

    /**
     * @param string $fileName
     * @return void
     */  
    public function downloadAction($fileName) {

        $file = $this->settings['uploadFolder'] . 'uploadedPhotos/' . $fileName;        

        if(is_file($file)) {

            $fileLen    = filesize($file);          
            $ext        = strtolower(substr(strrchr($fileName, '.'), 1));

            switch($ext) {
                case 'txt':
                    $cType = 'text/plain'; 
                break;              
                case 'pdf':
                    $cType = 'application/pdf'; 
                break;
                case 'zip':
                    $cType = 'application/zip';
                break;
                case 'doc':
                    $cType = 'application/msword';
                break;
                case 'xls':
                    $cType = 'application/vnd.ms-excel';
                break;
                case 'ppt':
                    $cType = 'application/vnd.ms-powerpoint';
                break;
                case 'gif':
                    $cType = 'image/gif';
                break;
                case 'png':
                    $cType = 'image/png';
                break;
                case 'jpeg':
                case 'jpg':
                    $cType = 'image/jpg';
                break;
                case 'mp3':
                    $cType = 'audio/mpeg';
                break;
                case 'wav':
                    $cType = 'audio/x-wav';
                break;
                case 'mpeg':
                case 'mpg':
                case 'mpe':
                    $cType = 'video/mpeg';
                break;
                case 'mov':
                    $cType = 'video/quicktime';
                break;
                case 'avi':
                    $cType = 'video/x-msvideo';
                break;

                //forbidden filetypes
                case 'inc':
                case 'conf':
                case 'sql':                 
                case 'cgi':
                case 'htaccess':
                case 'php':
                case 'php3':
                case 'php4':                        
                case 'php5':
                exit;

                case 'exe':                 
                default:
                    $cType = 'application/octet-stream';
                break;
            }

            $headers = array(
                'Pragma'                    => 'public', 
                'Expires'                   => 0, 
                'Cache-Control'             => 'must-revalidate, post-check=0, pre-check=0',                    
                'Content-Description'       => 'File Transfer',
                'Content-Type'              => $cType,
                'Content-Disposition'       => 'attachment; filename="'. $fileName .'"',
                'Content-Transfer-Encoding' => 'binary', 
                'Content-Length'            => $fileLen         
            );

            foreach($headers as $header => $data)
                $this->response->setHeader($header, $data); 

            $this->response->sendHeaders();                 
            @readfile($file);   

        }   
        exit;   
    }

I did this in a project some months ago, it's pretty straight forward.

    /**
     * @param string $fileName
     * @return void
     */  
    public function downloadAction($fileName) {

        $file = $this->settings['uploadFolder'] . 'uploadedPhotos/' . $fileName;        

        if(is_file($file)) {

            $fileLen    = filesize($file);          
            $ext        = strtolower(substr(strrchr($fileName, '.'), 1));

            switch($ext) {
                case 'txt':
                    $cType = 'text/plain'; 
                break;              
                case 'pdf':
                    $cType = 'application/pdf'; 
                break;
                case 'zip':
                    $cType = 'application/zip';
                break;
                case 'doc':
                    $cType = 'application/msword';
                break;
                case 'xls':
                    $cType = 'application/vnd.ms-excel';
                break;
                case 'ppt':
                    $cType = 'application/vnd.ms-powerpoint';
                break;
                case 'gif':
                    $cType = 'image/gif';
                break;
                case 'png':
                    $cType = 'image/png';
                break;
                case 'jpeg':
                case 'jpg':
                    $cType = 'image/jpg';
                break;
                case 'mp3':
                    $cType = 'audio/mpeg';
                break;
                case 'wav':
                    $cType = 'audio/x-wav';
                break;
                case 'mpeg':
                case 'mpg':
                case 'mpe':
                    $cType = 'video/mpeg';
                break;
                case 'mov':
                    $cType = 'video/quicktime';
                break;
                case 'avi':
                    $cType = 'video/x-msvideo';
                break;

                //forbidden filetypes
                case 'inc':
                case 'conf':
                case 'sql':                 
                case 'cgi':
                case 'htaccess':
                case 'php':
                case 'php3':
                case 'php4':                        
                case 'php5':
                exit;

                case 'exe':                 
                default:
                    $cType = 'application/octet-stream';
                break;
            }

            $headers = array(
                'Pragma'                    => 'public', 
                'Expires'                   => 0, 
                'Cache-Control'             => 'must-revalidate, post-check=0, pre-check=0',                    
                'Content-Description'       => 'File Transfer',
                'Content-Type'              => $cType,
                'Content-Disposition'       => 'attachment; filename="'. $fileName .'"',
                'Content-Transfer-Encoding' => 'binary', 
                'Content-Length'            => $fileLen         
            );

            foreach($headers as $header => $data)
                $this->response->setHeader($header, $data); 

            $this->response->sendHeaders();                 
            @readfile($file);   

        }   
        exit;   
    }
別甾虛僞 2024-11-06 16:36:57

您可以为下载请求定义特殊的 PageType:

download = PAGE
download  {
  typeNum = 1249058993
  10 < tt_content.list.20.efempty_pi1

  config {
   disableAllHeaderCode = 1
   xhtml_cleaning = 0
   admPanel = 0
   additionalHeaders = Content-type:application/octet-stream
    }
  }
}

“efempty”必须替换为您的扩展名。

您现在可以像这样触发下载:

<f:link.action pageType="1249058993" action="download" controller="ControllerName">Download</f:link.action>

You can define a special PageType for download requests:

download = PAGE
download  {
  typeNum = 1249058993
  10 < tt_content.list.20.efempty_pi1

  config {
   disableAllHeaderCode = 1
   xhtml_cleaning = 0
   admPanel = 0
   additionalHeaders = Content-type:application/octet-stream
    }
  }
}

"efempty" must be replaced by your extension's name.

You are now able to trigger the download like this:

<f:link.action pageType="1249058993" action="download" controller="ControllerName">Download</f:link.action>
谈场末日恋爱 2024-11-06 16:36:57

更新
这实际上不适用于激活的压缩,因为内容长度仍代表未压缩的大小。 eID 可以用作解决方法,事实上甚至有一个官方调用:

eID = "dumpFile" 请参阅typo3/sysext/core/Resource/PHP/FileDumpEID.php

但是,此调用不会强制下载,而是“转储”它。我已经在 Forge 上提交了一张票来解决这个问题(该票已被接受并且针对 LTS 7): https: //forge.typo3.org/issues/67111

旧答案
至于现在实现这一点的最简单方法是:

TypoScript Constants

# ASCII "download" in Numbers (4-15-23-14-12-15-1-4) - See http://rumkin.com/tools/cipher/numbers.php
plugin.tx_extensionname.view.formatToPageTypeMapping.download = 4152314121514

TypoScript Setup

tx_extensionname_download = PAGE
tx_extensionname_download {
    typeNum < plugin.tx_extensionname.view.formatToPageTypeMapping.download
    config {
        disableAllHeaderCode = 1
        xhtml_cleaning = 0
        admPanel = 0
        debug = 0
        no_cache = 1
    }

    10 = USER
    10 {
        userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
        extensionName = ExtensionName
        pluginName = PluginName
        vendorName = VENDOR
        controller = ControllerName
        action = download

        view < plugin.tx_extensionname.view
        persistence < plugin.tx_extensionname.persistence
        settings < plugin.tx_extensionname.settings
    }
}

Controller Action

/**
 * Download
 *
 * @param \VENDOR\ExtensionName\Domain\Model\Model $model
 * @return void
 * @ignorevalidation $model
 */
public function downloadAction($model) {
    if ($model->getFile()) {
        $model->getFile()->getOriginalResource()->getOriginalFile()->getStorage()->dumpFileContents(
            $model->getFile()->getOriginalResource(),
            $asDownload = TRUE,
            $alternativeFilename = $model->getFile()->getOriginalResource()->getName()
        );
        exit;
    }

    $this->throwStatus(
        $statusCode = 404,
        $statusMessage = 'Not Found'
    );
}

Fluid

<f:link.action controller="ControllerName" pluginName="PluginName" action="download" arguments="{model: '{model.uid}'}" format="download" title="{model.file.originalResource.title}">Download</f:link.action>

与其他提到的答案相比的好处如下:

  • 正确的标头(包括 mime/type)
  • Extbase 格式而不是 typenum

旁注:iPhone 忽略内容处置标题(始终内联)

关于“退出”问题,我还没有测试过它,但对于页面类型,如果您使用自己的 PHPView(StandaloneView?),它可能会起作用。

Update:
This actually doesn't work with activated compression because the Content-Length will still represent the uncompressed size. eID could be used as workaround in fact there is even a official call for that:

eID = "dumpFile" see typo3/sysext/core/Resource/PHP/FileDumpEID.php

However this call won't force a download but instead "dump" it. I've made a ticket @ Forge to fix this (which was accepted and is targeted for LTS 7): https://forge.typo3.org/issues/67111

Old answer:
As for now the easiest way to accomplish this is:

TypoScript Constants

# ASCII "download" in Numbers (4-15-23-14-12-15-1-4) - See http://rumkin.com/tools/cipher/numbers.php
plugin.tx_extensionname.view.formatToPageTypeMapping.download = 4152314121514

TypoScript Setup

tx_extensionname_download = PAGE
tx_extensionname_download {
    typeNum < plugin.tx_extensionname.view.formatToPageTypeMapping.download
    config {
        disableAllHeaderCode = 1
        xhtml_cleaning = 0
        admPanel = 0
        debug = 0
        no_cache = 1
    }

    10 = USER
    10 {
        userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
        extensionName = ExtensionName
        pluginName = PluginName
        vendorName = VENDOR
        controller = ControllerName
        action = download

        view < plugin.tx_extensionname.view
        persistence < plugin.tx_extensionname.persistence
        settings < plugin.tx_extensionname.settings
    }
}

Controller Action

/**
 * Download
 *
 * @param \VENDOR\ExtensionName\Domain\Model\Model $model
 * @return void
 * @ignorevalidation $model
 */
public function downloadAction($model) {
    if ($model->getFile()) {
        $model->getFile()->getOriginalResource()->getOriginalFile()->getStorage()->dumpFileContents(
            $model->getFile()->getOriginalResource(),
            $asDownload = TRUE,
            $alternativeFilename = $model->getFile()->getOriginalResource()->getName()
        );
        exit;
    }

    $this->throwStatus(
        $statusCode = 404,
        $statusMessage = 'Not Found'
    );
}

Fluid

<f:link.action controller="ControllerName" pluginName="PluginName" action="download" arguments="{model: '{model.uid}'}" format="download" title="{model.file.originalResource.title}">Download</f:link.action>

The benefits vs the other mentioned answers are as following:

  • Proper headers (including mime/type)
  • Extbase formats instead typenum

Side note: iPhone ignores the Content-Disposition headers (always inline)

About the "exit" concerns i've not tested it but with the page type it might work if you would use a own PHPView (StandaloneView?).

内心旳酸楚 2024-11-06 16:36:57

如何转储创建的下载文件并发送下载标头而不是正常的渲染过程?

只要标头尚未发送,尽管您可能想要 检查输出缓冲检查将发送的标头列表

How can I dump the created download-File and send a download header instead of the normal render process?

As long as headers haven't already been sent, there is nothing at all stopping you from emitting your headers, sending the content and actually calling exit, though you might want to check for output buffering and clear any open buffers before doing so. You might also want to inspect the list of headers that will be sent.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文