Completed
Pull Request — master (#3)
by
unknown
02:17
created

S3BucketStreamZip   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 114
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 33.33%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 6
c 3
b 1
f 0
lcom 1
cbo 3
dl 0
loc 114
ccs 14
cts 42
cp 0.3333
rs 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
A send() 0 56 3
A __construct() 0 20 3
1
<?php
2
/**
3
* @author Jaisen Mathai <[email protected]>
4
* @copyright Copyright 2015, Jaisen Mathai
5
*
6
* This library streams the contents from an Amazon S3 bucket
7
*  without needing to store the files on disk or download
8
*  all of the files before starting to send the archive.
9
*
10
* Example usage can be found in the examples folder.
11
*/
12
namespace JMathai\S3BucketStreamZip;
13
14
use Aws\S3\S3Client;
15
use JMathai\S3BucketStreamZip\Exception\InvalidParameterException;
16
use ZipStream;
17
18
class S3BucketStreamZip
19
{
20
    /**
21
     * @var array
22
     *
23
     * http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html
24
     *
25
     * {
26
     *   key: access key,
27
     *   secret: access secret
28
     *   bucket: bucket name
29
     *   region: bucket region
30
     *   prefix: prefix
31
     * }
32
     */
33
    private $params = [];
34
35
    /**
36
     * @var object
37
     */
38
    private $s3Client;
39
40
    /**
41
     * Create a new ZipStream object.
42
     *
43
     * @param array $params - AWS key, secret, region, and list object parameters
44
     */
45 4
    public function __construct($params)
46
    {
47 4
        foreach (['key', 'secret', 'bucket', 'region'] as $key) {
48 4
            if (!isset($params[$key])) {
49 3
                throw new InvalidParameterException('$params parameter to constructor requires a `'.$key.'` attribute');
50
            }
51 3
        }
52
53 1
        $this->params = $params;
54
55 1
        $this->s3Client = new S3Client(
56
            [
57 1
                'region'      => $this->params['region'],
58 1
                'version'     => 'latest',
59
                'credentials' => [
60 1
                'key'    => $this->params['key'],
61 1
                'secret' => $this->params['secret'],
62 1
            ],
63 1
        ]);
64 1
    }
65
66
    /**
67
     * Stream a zip file to the client.
68
     *
69
     * @param string $filename - Name for the file to be sent to the client
70
     * @param array  $params   - Optional parameters
71
     *                         {
72
     *                         expiration: '+10 minutes'
73
     *                         }
74
     */
75
    public function send($filename, $params = [])
76
    {
77
        // Set default values for the optional $params argument
78
        if (!isset($params['expiration'])) {
79
            $params['expiration'] = '+10 minutes';
80
        }
81
82
        // Initialize the ZipStream object and pass in the file name which
83
        //  will be what is sent in the content-disposition header.
84
        // This is the name of the file which will be sent to the client.
85
        $zip = new ZipStream\ZipStream($filename);
86
87
        // Get a list of objects from the S3 bucket. The iterator is a high
88
        //  level abstration that will fetch ALL of the objects without having
89
        //  to manually loop over responses.
90
        $result = $this->s3Client->getIterator('ListObjects', [
91
            'Bucket' => $this->params['bucket'],
92
            'Prefix' => $this->params['prefix'],
93
        ]);
94
95
        // We loop over each object from the ListObjects call.
96
        foreach ($result as $file) {
97
            // We need to use a command to get a request for the S3 object
98
            //  and then we can get the presigned URL.
99
            $command = $this->s3Client->getCommand('GetObject', [
100
                'Bucket' => $this->params['bucket'],
101
                'Key'    => $file['Key'],
102
            ]);
103
            $signedUrl = (string) $this->s3Client->createPresignedRequest($command, $params['expiration'])->getUri();
104
105
106
            // Get the file name on S3 so we can save it to the zip file
107
            //  using the same name.
108
            $fileName = substr($file['Key'], strlen($this->params['prefix']));
109
110
            // We want to fetch the file to a file pointer so we create it here
111
            //  and create a curl request and store the response into the file
112
            //  pointer.
113
            // After we've fetched the file we add the file to the zip file using
114
            //  the file pointer and then we close the curl request and the file
115
            //  pointer.
116
            // Closing the file pointer removes the file.
117
            $fp = tmpfile();
118
            $ch = curl_init($signedUrl);
119
            curl_setopt($ch, CURLOPT_TIMEOUT, 120);
120
            curl_setopt($ch, CURLOPT_FILE, $fp);
121
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
122
            curl_exec($ch);
123
            curl_close($ch);
124
            $zip->addFileFromStream($fileName, $fp);
125
            fclose($fp);
126
        }
127
128
        // Finalize the zip file.
129
        $zip->finish();
130
    }
131
}
132