1

我有一个 symfony 2.8 应用程序,在用户单击“下载”按钮时,我使用 s3 上的几个大(图像、视频)文件的密钥使用 ZipStream(https://github. com/maennchen/ZipStream-PHP)。

文件流和下载为 zip(存储,未压缩)在浏览器中是成功的,并且 zip 位于下载中。但是,当尝试使用 Archive Utility(OSX 上的本机存档软件)在 Mac OSX El Capitan 中打开 zip 时,会出错。错误:

无法将“test.zip”扩展为“下载”。(错误 2 - 没有这样的文件或目录。)

我在 SO 上看到了较旧的相同问题并尝试了这些修复,特别是这篇文章:https : //stackoverflow.com/a/5574305/136151 并跟进了 ZipStream 中相关的问题和 PRs 以及 Guzzle 等中的上游修复.

问题是,上面的修复是在 2011 年,当时事情还在继续。因此,应用相同的修复程序我没有得到有效的结果。

我尝试过的具体修复是: 1. 按照建议将“要提取的版本”设置为 0x000A。也如另一篇文章中推荐的“20”。我为“制作的版本”设置了相同的设置。2. 我试图强制压缩方法“放气”而不是“存储”,看看我是否得到了工作结果。存储的结果就是我所需要的,并且适合用作图像和视频的容器文件的 zip。

我可以使用名为 The Unarchiver 的第三方存档应用程序提取 zip。但是,用户不会知道也不能期望安装替代存档应用程序以适合我的网络应用程序。那不是一个有效的解决方案。

有没有人有解决这个问题的知识或经验,可以帮助我解决这个问题?

注意:流式压缩到浏览器是必需的解决方案。考虑到这种方法的时间和开销,将资产从 s3 下载到服务器以创建一个 zip,然后将生成的 zip 流式传输到浏览器并不是一个解决方案。

如果需要,添加信息:

代码和设置: - 文件存储在 s3 上。- Web 应用程序是 PHP7.0 上的 symfony 2.8,在 ec2 和 Ubuntu 上运行。- 使用 aws-sdk-php,创建一个具有有效凭据的 s3client,然后我在 s3client 上注册 StreamWrapper (s3client->registerStreamWrapper())。这是通过 fopen 从 s3 获取文件以流式传输到 ZipStream 库:

$this->s3Client = $s3Client;  
$this->s3Client->registerStreamWrapper();

// Initialize the ZipStream object and pass in the file name which
//  will be what is sent in the content-disposition header.
// This is the name of the file which will be sent to the client.
// Define suitable options for ZipStream Archive.
$opt = array(
        'comment' => 'test zip file.',
        'content_type' => 'application/octet-stream'
      );
$zip = new ZipStream\ZipStream($filename, $opt);

$keys = array(
      "zipsimpletestfolder/file1.txt"
  );

foreach ($keys as $key) {
    // Get the file name in S3 key so we can save it to the zip file 
    // using the same name.
    $fileName = basename($key);

    $bucket = 'mg-test';
    $s3path = "s3://" . $bucket . "/" . $key;

    if ($streamRead = fopen($s3path, 'r')) {
      $zip->addFileFromStream($fileName, $streamRead);        
    } else {
      die('Could not open stream for reading');
    }
}

$zip->finish();

压缩输出结果:

  • 通过存档实用程序在 mac 上提取失败并出现错误 2

  • 在 Mac 上使用 Unarchiver 进行提取。

  • 使用 7-zip 在 Windows 上进行提取。

  • 使用 WinRar 在 Windows 上提取失败 - 说 zip 已损坏。

响应标头:

在此处输入图像描述

编辑 我愿意使用另一种将文件作为 zip 流式传输到浏览器的方法,如果建议,可以在 Mac、Windows、Linux 上本地打开,而无需使用 ZipStream。只是不要创建一个 zip 文件服务器端以随后进行流式传输。

4

1 回答 1

4

下载的 zip 的问题在于它包含来自 Symfony 控制器中调用 ZipStream->addFileFromStream() 的响应的 html。基本上,当 ZipStream 在客户端浏览器中流式传输数据以创建 zip 下载时,还会发送控制器操作响应,并且最好的猜测是,两者在客户端浏览器上混淆了。在十六进制编辑器中打开 zip 文件并在其中看到 html,这显然是问题所在。

为了在带有 ZipStream 的 Symfony 2.8 中实现这一点,我只是在控制器操作中使用了 Symfony 的 StreamedResponse,并在 streamedResponse 的函数中使用了 ZipStream。要流式传输 S3 文件,我只需将 s3keys 和 s3client 数组传入该函数。所以:

use Symfony\Component\HttpFoundation\StreamedResponse;
use Aws\S3\S3Client;    
use ZipStream;

//...

/**
 * @Route("/zipstream", name="zipstream")
 */
public function zipStreamAction()
{
    //test file on s3
    $s3keys = array(
      "ziptestfolder/file1.txt"
    );

    $s3Client = $this->get('app.amazon.s3'); //s3client service
    $s3Client->registerStreamWrapper(); //required

    $response = new StreamedResponse(function() use($s3keys, $s3Client) 
    {

        // Define suitable options for ZipStream Archive.
        $opt = array(
                'comment' => 'test zip file.',
                'content_type' => 'application/octet-stream'
              );
        //initialise zipstream with output zip filename and options.
        $zip = new ZipStream\ZipStream('test.zip', $opt);

        //loop keys useful for multiple files
        foreach ($s3keys as $key) {
            // Get the file name in S3 key so we can save it to the zip 
            //file using the same name.
            $fileName = basename($key);

            //concatenate s3path.
            $bucket = 'bucketname';
            $s3path = "s3://" . $bucket . "/" . $key;        

            //addFileFromStream
            if ($streamRead = fopen($s3path, 'r')) {
              $zip->addFileFromStream($fileName, $streamRead);        
            } else {
              die('Could not open stream for reading');
            }
        }

        $zip->finish();

    });

    return $response;
}

解决了。也许这有助于其他人在 Symfony 中使用 ZipStream。

于 2017-06-22T18:00:16.003 回答