Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Download large file

Hi,

in my application, I creating zip archive with various sizes and I need to be able to have an action that allows the user to download this file. The issue I am struggling with is that if the file is too big, it will cut the downloading at 65 MB thus corrupting the archive. If I open this corrupted archive in an editor, there is PHP error "Allowed memory exceeded". Is there a way how can I send the file for download without loading its content in memory?

Either set up some static files/ folder to where you generate your zip files, then just propagate that url as the download link.

Or use PHP's fpasshtru()

I can't have this zip archive availible for everyone, there are some confidential data in there so I have to check for permissions before I can send it to the user. I tried the fpassthru() but it did not solve the issue.

edited 23d ago
class DownloadController extends PhController {
    public function indexAction() {
        $this->view->disable();
        $filePath = 'some/protected/file.zip';
        $this->response->setFileToSend($filePath, 'download_name.zip');
        $this->response->send();
    }
}
edited 23d ago

I have already tried this approach before, the issue is that even tho it starts to download, the process of downloading is always interrupted at exactly 65 MB.

EDIT: When I tried to download the same file by using simple plain php script in public directory, I was able to download the whole file without any problem, but when I download it using an controller action, it will die at 65 MB. https://i.imgur.com/YsAHd7O.png

Weird... what if you put that plain PHP code into a controller action?

class DownloadController extends PhController {
    public function indexAction() {
        $this->view->disable();
        $filePath = 'some/protected/file.zip';
        $fileName = basename($path);

        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $filePath);

        header("Content-Disposition: attachment; filename=$fileName;");
        header("Content-Type: $mime");
        header('Content-Length: ' . filesize($path));
        $fh = fopen($filePath, 'rb');
        fpassthru($fh);
        fclose(fh);
        exit;
    }
}
edited 23d ago

Same thing, interrupt at 65 MB because of exceeded memory:

    public function archiveDownloadFileAction() {
        $this->view->disable();
        $filePath = "path/to/the/file";

        header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header('Content-Description: File Transfer');
        header('Content-type: application/zip');
        header('Content-Disposition: attachment; filename="filename.zip"');

        readfile($filePath);
        die();
    }


61.6k
Accepted
answer
edited 23d ago

There's something wrong in your setup, fpassthru streams the file handle directly to the output, the memory footprint should be minimal.

Try clearing the output buffer:

class DownloadController extends PhController {
    public function indexAction() {
        $this->view->disable();
        ob_get_clean();
        ob_end_flush();

        $filePath = 'some/protected/file.zip';
        $fileName = basename($path);

        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $filePath);

        header("Content-Disposition: attachment; filename=$fileName;");
        header("Content-Type: $mime");
        header('Content-Length: ' . filesize($path));
        $fh = fopen($filePath, 'rb');
        fpassthru($fh);
        fclose($fh);
        exit;
    }
}


5.8k

You can also have a look at xsendfile for apache or nginx

I have tried that already with no success.

You can also have a look at xsendfile for apache or nginx

This finally helped, thank you very much!

There's something wrong in your setup, fpassthru streams the file handle directly to the output, the memory footprint should be minimal.

Try clearing the output buffer:

class DownloadController extends PhController {
   public function indexAction() {
       $this->view->disable();
      ob_get_clean();
      ob_end_flush();

       $filePath = 'some/protected/file.zip';
       $fileName = basename($path);

       $finfo = finfo_open(FILEINFO_MIME_TYPE);
       $mime = finfo_file($finfo, $filePath);

       header("Content-Disposition: attachment; filename=$fileName;");
       header("Content-Type: $mime");
       header('Content-Length: ' . filesize($path));
       $fh = fopen($filePath, 'rb');
       fpassthru($fh);
       fclose($fh);
       exit;
   }
}