使用php从存储在S3上的文件动态创建zip文件

cub*_*war 6 php amazon-s3 zipfile

我有一个Laravel网络应用程序,用户可以在其中上传文件.这些文件可能很敏感,虽然它们存储在S3上,但只能通过我的网络服务器访问(流下载).上传后,用户可能希望下载这些文件的选择.

以前,当用户下载一系列文件时,我的Web服务器会从S3下载文件,在本地压缩,然后将zip发送到客户端.但是,由于文件大小一旦投入生产,服务器响应会经常超时.

作为一种替代方法,我想通过ZipStream动态压缩文件,但我没有太多运气.zip文件最终会损坏文件,或者自身损坏并且非常小.

如果可以将S3上的文件的流资源传递给ZipStream,解决超时问题的最佳方法是什么?

我尝试了几种方法,我最近的两种方法如下:

// First method using fopen
// Results in tiny corrupt zip files
if (!($fp = fopen("s3://{$bucket}/{$key}", 'r')))
{
    die('Could not open stream for reading');
}

$zip->addFileFromPath($file->orginal_filename, "s3://{$bucket}/{$key}");
fclose($fp);


// Second method tried get download the file from s3 before sipping
// Results in a reasonable sized zip file that is corrupt
$contents = file_get_contents("s3://{$bucket}/{$key}");

$zip->addFile($file->orginal_filename, $contents); 
Run Code Online (Sandbox Code Playgroud)

每个都位于一个遍历每个文件的循环中.在循环之后我调用$ zip-> finish().

注意我没有得到任何PHP错误只是损坏文件.

cub*_*war 5

最后,解决方案是使用签名的 S3 url 和 curl 为 ZipStream 提供文件流,如s3 bucket steam zip php 所示。从上述来源编辑的结果代码如下:

public function downloadZip()
{
    // ...

    $s3 = Storage::disk('s3');
    $client = $s3->getDriver()->getAdapter()->getClient();
    $client->registerStreamWrapper();
    $expiry = "+10 minutes";

    // Create a new zipstream object
    $zip = new ZipStream($zipName . '.zip');

    foreach($files as $file)
    {
        $filename = $file->original_filename;

        // We need to use a command to get a request for the S3 object
        //  and then we can get the presigned URL.
        $command = $client->getCommand('GetObject', [
            'Bucket' => config('filesystems.disks.s3.bucket'),
            'Key' => $file->path()
        ]);

        $signedUrl = $request = $client->createPresignedRequest($command, $expiry)->getUri();

        // We want to fetch the file to a file pointer so we create it here
        //  and create a curl request and store the response into the file
        //  pointer.
        // After we've fetched the file we add the file to the zip file using
        //  the file pointer and then we close the curl request and the file
        //  pointer.
        // Closing the file pointer removes the file.
        $fp = tmpfile();
        $ch = curl_init($signedUrl);
        curl_setopt($ch, CURLOPT_TIMEOUT, 120);
        curl_setopt($ch, CURLOPT_FILE, $fp);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_exec($ch);
        curl_close($ch);
        $zip->addFileFromStream($filename, $fp);
        fclose($fp);
    }

    $zip->finish();
}
Run Code Online (Sandbox Code Playgroud)

请注意,这需要在您的服务器上安装并运行 curl 和 php-curl。