下载 ZipArchive OutOfMemory

Download ZipArchive OutOfMemory

我在 heroku 应用程序上下载文件时遇到问题。 我有一个基本的 PHP ini,它允许我们有 128MB,用于内存限制。

我的错误如下:

(1/1) OutOfMemoryException Error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 77709312 bytes)

好的,我明白了,但奇怪的是 77709312bytes 只有 74MB。

怎么了?

这是我的下载操作代码:

/**
 * @Route("/templates/{id}/download", name="api_templates_download", requirements={"id"="\d+"})
 * @Method({"GET"})
 * @param \Symfony\Component\HttpFoundation\Request $request
 * @return \Symfony\Component\HttpFoundation\Response
 * @throws \Exception
 */
public function downloadTemplate(Request $request)
{
    $id = $request->get('id');

    try {
        $template = $this->service->getTemplate($id, $this->helper->getUser());
        $archive = $this->service->downloadTemplate($template);
        $response = new Response(file_get_contents($archive));
        $response->headers->set('Content-Type', 'application/zip');
        $response->headers->set('Content-Disposition', 'attachment;filename="' . $archive . '"');
        $response->headers->set('Content-length', filesize($archive));

        return $response;
    } catch (\Exception $e) {
        $response = new Response(json_encode($e->getMessage()), 401);
        $response->headers->set('Content-Type', 'application/json');
        return $response;
    }
}

以及在控制器中调用的方法downloadTemplate

/**
 * @tests Need to tests about performance - do this in September with real server
 * @param \App\Domain\Dollycast\Template\Entity\Template $template
 * @return string
 */
public function downloadTemplate(Template $template)
{
    $zip = new \ZipArchive();
    $zipName = self::ZIPDIR . $template->getName() . '.zip';

    $zip->open($zipName, \ZipArchive::CREATE);

    $finder = new Finder();
    $finder->files()->in(self::WORKDIR . $template->getName());

    $zip->addEmptyDir($template->getName());
    /** @var SplFileInfo $file */
    foreach ($finder as $file) {
        // Rename the full path to the relative Path
        $zip->addFile(
            self::WORKDIR . $template->getName() . DIRECTORY_SEPARATOR . $file->getRelativePathname(),
            $template->getName() . DIRECTORY_SEPARATOR . $file->getRelativePathname()
        );
    }

    $zip->close();

    return $zipName;
}

(1/1) OutOfMemoryException Error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 77709312 bytes)

当时,php 进程尝试分配 77709312 字节,它已经分配了内存,可以使用函数 memory_get_usage() 检查。在您的情况下,这必须超过 (134217728 - 77709312 =) 56508416 字节。

如果在创建zip文件的过程中出现异常,您可以尝试使用外部工具来创建zip文件。像

exec("tar -zcvf archive.tar.gz /folder/tozip");

否则,请尝试查明此异常的确切抛出时间,并尝试释放所有不必要的已用内存。

file_get_contents 会将文件加载到内存中,当我将其放入 Response 对象时,这将有效地将所有内容从内存复制到输出缓冲区。这就是为什么 74MB 附近的存档会由于双重工作而导致 Out of memory for 128M 配置 (74*2) 错误。

此时,我无法使用 readfile() 来解决这个问题,因为行为与我的需要有太多不同。但是 readfile() 将打开并输出到缓冲区而不使用两倍的内存。

编辑:我发现最好的选择是使用 Symfony 3.2 中的 BinaryFileResponse 对象,它可以有效地处理响应流。

你试过了吗file_get_contents($zipName); unlink($zipName);所以你可以从本地删除文件