在Symfony中使用ZipStream:流式压缩下载不会在Mac OSX上使用Archive Utility进行解压缩

时间:2017-06-22 10:48:32

标签: php macos symfony amazon-s3 zipstream

我有一个symfony 2.8应用程序,在用户点击“下载”按钮时,我使用s3上的几个大(图像,视频)文件的密钥,使用ZipStream将其作为zip文件传输到浏览器({{3 }})。

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

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

我在SO上看过较旧的,相同的问题,并尝试了这些修补程序,特别是这篇文章:https://github.com/maennchen/ZipStream-PHP并对问题& ZipStream中的PR与Guzzle等中的上游修复有关。

问题是,上述修复工作已于2011年重新开始,并且事情正在进行中。因此,应用相同的修复程序,我无法获得有效的结果。

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

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

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

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

根据需要添加信息:

代码&设置:   - 文件存储在s3上。   - Web应用程序是PHP7.0上的symfony 2.8,在使用Ubuntu的ec2上运行。   - 使用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();

Zip输出结果:

  • 通过Archive Utility在mac上提取失败,错误2

  • 使用unarchiver工作,在Mac上进行提取。

  • 使用7-zip工作在窗户上进行提取。

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

响应标题:

https://stackoverflow.com/a/5574305/136151

修改 我愿意使用另一种将文件流式传输到浏览器的方法作为zip,可以在Mac,Windows,Linux上本地打开而不使用ZipStream(如果建议的话)。只是不创建一个zip文件服务器端以便随后流式传输。

1 个答案:

答案 0 :(得分:2)

下载zip的问题是它包含来自调用ZipStream-> addFileFromStream()的Symfony控制器中的响应的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。