Project

General

Profile

Actions

Bug #69223

closed

dumpFileContents does not work when ['FE']['compressionLevel'] > 0

Added by Torben Hansen over 9 years ago. Updated over 4 years ago.

Status:
Closed
Priority:
Should have
Assignee:
-
Category:
File Abstraction Layer (FAL)
Start date:
2015-08-21
Due date:
% Done:

0%

Estimated time:
TYPO3 Version:
7
PHP Version:
Tags:
Complexity:
Is Regression:
No
Sprint Focus:

Description

When ['FE']['compressionLevel'] > 0, dumpFileContents does not output any file, but throws the following exception:

Core: Exception handler (WEB): Uncaught TYPO3 Exception: #1: PHP Warning: Cannot modify header information - headers already sent in /path/to/typo3_src/6.2/typo3_src-6.2.14/typo3/sysext/frontend/Classes/Utility/CompressionUtility.php

How to reproduce:

1. Set ['FE']['compressionLevel'] = 5
2. Dump a file from FAL like shown below

$storage = $this->resourceFactory->getDefaultStorage();
$file = $storage->getFile('test.jpg');
$storage->dumpFileContents($file, TRUE, 'test-filename.jpg');
Actions #1

Updated by Frans Saris over 9 years ago

  • Status changed from New to Needs Feedback

Also when you put a exit; right after the dump?

We use this in https://github.com/beechit/fal_securedownload/blob/master/Classes/Hooks/FileDumpHook.php and can remember any issues. But maybe that is due the fact it used in the eId context.

Can you determine where the first header is send?

Actions #2

Updated by Torben Hansen over 9 years ago

Adding exit; does not solve the problem. I believe, that the first header gets send by ResourceStorage->dumpFileContents(), because if I comment out the header sent by the compression utility, then the download gets triggered. Here the next problem appears, which is a following problem of the compression.

When compression is enabled, the ResourceStorage->dumpFileContents() actually sends the correct header information for the file, but the compression messes this up. I'll dump an .ics file and ResourceStorage->dumpFileContents() sets the mime type correctly to "text/calendar". When compression is enabled, this mime type sent in the headers does'nt match the mime type of the sent file, so the browser refuses to download the file.

Resource interpreted as Document but transferred with MIME type text/calendar

IMO dumpFileContents() should ignore FE compression and just dump the file as it is.

Actions #3

Updated by Frans Saris over 9 years ago

If I read the code correct the compression is disabled in $storage->dumpFileContents. Further are the headers send by the dumpFileContents the last headers send. Before the file is dumped the output buffer is cleared and after that no compression is set. See ResourceStorage::dumpFileContents().

So what is already outputted before you call $storage->dumpFileContents?

Actions #4

Updated by Torben Hansen over 9 years ago

My code is really simple.

I have an ExtBase controller, where I have the following action (simplyfied).

public function downloadAction() {
    $storage = $this->resourceFactory->getDefaultStorage();
    $file = $storage->getFile('test.jpg');
    $storage->dumpFileContents($file, TRUE, 'test-filename.jpg');
    return FALSE;
}

This all works well, when FE compression is disabled. When I enable FE compression, the exception is thrown. I get the following headers as a response from the server

HTTP/1.1 200 OK
Date: Fri, 21 Aug 2015 11:17:39 GMT
Server: Apache/2.2.29 (Unix) mod_ssl/2.2.29 OpenSSL/1.0.2a DAV/2 PHP/5.6.9
X-Powered-By: PHP/5.6.9
Expires: 0
Last-Modified: Fri, 21 Aug 2015 11:17:39 GMT
Cache-Control: ''
Pragma: no-cache
Content-Disposition: attachment; filename="event_1.ics" 
Content-Length: 281
Content-Encoding: gzip
Vary: Accept-Encoding
Keep-Alive: timeout=5, max=99
Connection: Keep-Alive
Content-Type: text/calendar;charset=UTF-8

For me, everything seems fine except the Content-Encoding: gzip header. The Content-Length header is ok and outputs the content length for the uncompressed file. So I wonder where this Content-Encoding: gzip header comes from.

Does the ExtBase action send the Content-Encoding: gzip header and if yes, can it be avoided?

Actions #5

Updated by Torben Hansen about 9 years ago

Any idea how to solve this issue? It seems dumpFileContents for the frontend context only works correct, when FE compression is disabled. Any ideas for a workaround?

Actions #6

Updated by Mathias Schreiber about 9 years ago

  • Target version deleted (next-patchlevel)
Actions #7

Updated by Alexander Opitz over 8 years ago

  • Status changed from Needs Feedback to New
  • Target version set to next-patchlevel
Actions #8

Updated by Sven Teuber over 7 years ago

  • TYPO3 Version changed from 6.2 to 7

Issue is still present in TYPO3 7.6.19 LTS (bump)

The Content-Encoding: gzip header Torben mentioned, which is the source of the problem, seems to be set by PHP when zlib.output_compression_level is set in \TYPO3\CMS\Frontend\Http\RequestHandler::initializeOutputCompression. I guess it should be removed before dumping a file with dumpFileContents().

Actions #9

Updated by Susanne Moog over 5 years ago

  • Status changed from New to Needs Feedback

Can you check this behaviour with 9? Request/Repsonse handling got lots of changes, it might be fixed.

Actions #10

Updated by Torben Hansen over 5 years ago

Yes I verified this with TYPO3 9.5 and the problem is still valid, since the method dumpFileContents is still the same.

But anyway, dumpFileContents is deprecated since V9, so I tried to migrate to streamFile instead. Migration (especially using the functionality in an Extbase extension) is not really easy/possible(?), since the successor for dumpFileContents works different. dumpFileContents streams the file directly to the client using output buffering, but streamFile returns a PSR-7 response object.

As far as I know, Extbase has its on request/response handling, so you can't just return the PSR-7 response object in an Extbase action to stream the file to the client. Also I did not find something to emit the PSR7 response in Extbase and a search on Slack resulted in one single person asking for help about this topic, but with no response.

In TYPO3 backend, application::sendResponse is used to emit a PSR-7 response object. I implemented a similar method in my Extbase controller to force output of the content of the PSR-7 response, but honesty this feels dirty (see code below).

    public function downloadAction()
    {
        $storage = $this->resourceFactory->getDefaultStorage();
        $file = $storage->getFile('test.jpg');
        $response = $storage->streamFile($file, true, 'test-filename.jpg');
        $this->sendResponse($response);
        exit();
    }

    protected function sendResponse($response)
    {
        if (!headers_sent()) {
            if (http_response_code() === 200) {
                header('HTTP/' . $response->getProtocolVersion() . ' ' . $response->getStatusCode() . ' ' . $response->getReasonPhrase());
            }

            foreach ($response->getHeaders() as $name => $values) {
                header($name . ': ' . implode(', ', $values));
            }
        }
        $body = $response->getBody();
        echo $body->__toString();
    }

Is there a recommended way to emit PSR-7 response objects in Extbase? Or is there maybe something I am missing?

Actions #11

Updated by Riccardo De Contardi over 5 years ago

  • Status changed from Needs Feedback to New
Actions #12

Updated by Benni Mack over 5 years ago

  • Target version changed from next-patchlevel to Candidate for patchlevel
Actions #13

Updated by Benni Mack over 4 years ago

  • Status changed from New to Closed

@Torben you can throw an Exception (ImmediateResponseException) that contains a PSR-7 response that is the way that is currently possible but not clean (hopefully we'll get there).

I hope this information helps. I will close this issue - feel free to re-open if you want us to do something else code-wise. I have this topic (Extbase + PSR7 Response) on the radar for v11 anyway.

Actions

Also available in: Atom PDF