在Symfony中使用ZipStream:使用Mac OSX上的存档实用程序不会解压缩流式zip下载 [英] Using ZipStream in Symfony: streamed zip download will not decompress using Archive Utility on Mac OSX

查看:115
本文介绍了在Symfony中使用ZipStream:使用Mac OSX上的存档实用程序不会解压缩流式zip下载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个symfony 2.8应用程序,并且在用户单击下载"按钮时,我使用s3上的几个大型(图像,视频)文件的键,使用ZipStream将其作为zip文件流式传输到浏览器( https://github.com/maennchen/ZipStream-PHP ).

I have a symfony 2.8 application and on the user clicking "download" button, I use the keys of several large (images, video) files on s3 to stream this to the browser as a zip file using ZipStream (https://github.com/maennchen/ZipStream-PHP).

在浏览器中成功流式传输文件并以zip格式下载(存储但未压缩).该zip文件位于下载"中.但是,当尝试使用存档实用程序"(OSX上的本机存档软件)在Mac OSX El Capitan中打开zip时,它会出错.错误:

The streaming of files and download as a zip (stored, not compressed) is successful in the browser & the zip lands in Downloads. However, when attempting to open the zip in Mac OSX El Capitan using Archive Utility (native archive software on OSX), it errors out. The Error:

无法将"test.zip"扩展为下载". (错误2-没有此类文件或目录.)

我在SO上看到了较旧的相同问题,并尝试了这些修复,尤其是这篇文章: https://stackoverflow.com/a /5574305/136151 ,并就问题与解决方案"进行了跟踪相关的ZipStream中的PR和Guzzle等中的上游修复程序.

I have seen older, identical issues on SO and attempted those fixes, specifically this post: https://stackoverflow.com/a/5574305/136151 and followed up on the Issues & PRs in ZipStream that relates and the upstream fixes in Guzzle etc.

问题是,以上修复程序可以追溯到2011年,当时情况还在继续.因此,应用相同的修复程序我没有得到有效的结果.

Problem is, the above fixes were back in 2011 and things move on in that time. So applying the same fixes I am not getting a working result.

我尝试过的特定修补程序是: 1.按照建议将提取版本"设置为0x000A.在另一篇文章中建议也设为"20".我为由...制作的版本"设置了相同的设置. 2.我试图迫使压缩方法缩小"而不是存储",以查看是否获得了有效的结果.存储的结果就是我所需要的,并且适合用作文件和图片容器文件的zip文件.视频.

Specific fixes I have tried is: 1. Setting "version to extract" to 0x000A as suggested. Also as '20' as recommended in another post. I've set the same for "version made by". 2. I tried to force the compression method to 'deflate' instead of 'stored' to see if I got a working result. A stored resulted is all I need and suitable for a zip used as container file for images & video.

我能够使用名为The Unarchiver的第三方存档应用程序提取zip.但是,用户不会知道&不能期望安装其他存档应用程序来适合我的Web应用程序.那不是一个有效的解决方案.

I am able to extract the zip using a third party archive app called The Unarchiver. However, users won't know & can't be expected to install an alternative archive app to suit my web app. Thats not an effective solution.

有人有解决此问题的知识或经验,可以帮助我解决该问题吗?

Does anyone have knowledge or experience of solving this issue and can help me out with how to resolve it?

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

NB: A streamed zip to browser is the required solution. Downloading assets from s3 to the server to create a zip and then stream the resulting zip to the browser is not a solution given the amount of time and overhead of such an approach.

根据需要添加了信息:

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

Code & Setup: - Files are stored on s3. - Web app is symfony 2.8 on PHP7.0 runing on ec2 with Ubuntu. - Using aws-sdk-php, create an s3client with valid credentials and I register StreamWrapper (s3client->registerStreamWrapper()) on the s3client. This is to fetch files from s3 via fopen to stream to ZipStream library:

$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上通过Archive Utility进行提取失败,并显示错误2

  • Extraction on mac via Archive Utility fails with error 2

在Mac上使用Unarchiver进行提取有效.

Extraction on mac with The unarchiver works.

在具有7-zip的Windows上进行提取.

Extraction on windows with 7-zip works.

使用WinRar在Windows上提取失败-表示zip损坏.

Extraction on windows with WinRar fails - says zip is corrupted.

响应标题:

修改 我愿意使用另一种将文件作为zip格式流式传输到浏览器的方法,可以在Mac,Windows和Linux上本机打开,而无需使用ZipStream(如果建议).只是不要创建一个zip文件服务器端,然后再进行流式传输.

Edit I'm open to using another method of streaming files to browser as a zip that can be opened on Mac, Windows, Linux natively without using ZipStream if suggested. Just not to creating a zip file server side to subsequently stream thereafter.

推荐答案

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

The issue with the zip downloaded is that it contained html from the response in the Symfony controller that was calling ZipStream->addFileFromStream(). Basically, when ZipStream was streaming data to create zip download in client browser, a controller action response was also sent and, best guess, the two were getting mixed up on client's browser. Opening the zip file in hex editor and seeing the html in there it was obviously the issue.

要使它在带有ZipStream的Symfony 2.8中工作,我只是在控制器动作中使用了Symfony的StreamedResponse,并在streamedResponse的函数中使用了ZipStream.为了流式传输S3文件,我只是将s3keys和s3client数组传递给该函数.所以:

To get this working in Symfony 2.8 with ZipStream, I just used Symfony's StreamedResponse in the controller action and used ZipStream in the streamedResponse's function. To stream S3 files I just passed in an array of s3keys and the s3client into that function. So:

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.

Solved. Maybe this helps someone else use ZipStream in Symfony.

这篇关于在Symfony中使用ZipStream:使用Mac OSX上的存档实用程序不会解压缩流式zip下载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆