Passed
Pull Request — master (#48)
by
unknown
02:19
created

OssAdapter::signatureConfig()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 76
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 7
Bugs 2 Features 0
Metric Value
cc 7
eloc 52
c 7
b 2
f 0
nc 10
nop 6
dl 0
loc 76
ccs 0
cts 16
cp 0
crap 56
rs 8.1138

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the iidestiny/flysystem-oss.
5
 *
6
 * (c) iidestiny <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Iidestiny\Flysystem\Oss;
13
14
use Carbon\Carbon;
15
use Iidestiny\Flysystem\Oss\Traits\SignatureTrait;
16
use League\Flysystem\AdapterInterface;
17
use League\Flysystem\Adapter\AbstractAdapter;
18
use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait;
19
use League\Flysystem\Config;
20
use OSS\Core\OssException;
21
use OSS\OssClient;
22
23
/**
24
 * Class OssAdapter.
25
 *
26
 * @author iidestiny <[email protected]>
27
 */
28
class OssAdapter extends AbstractAdapter
29
{
30
    use NotSupportingVisibilityTrait;
31
    use SignatureTrait;
32
33
    // 系统参数
34
    const SYSTEM_FIELD = [
35
        'bucket' => '${bucket}',
36
        'etag' => '${etag}',
37
        'filename' => '${object}',
38
        'size' => '${size}',
39
        'mimeType' => '${mimeType}',
40
        'height' => '${imageInfo.height}',
41
        'width' => '${imageInfo.width}',
42
        'format' => '${imageInfo.format}',
43
    ];
44
45
    /**
46
     * @var
47
     */
48
    protected $accessKeyId;
49
50
    /**
51
     * @var
52
     */
53
    protected $accessKeySecret;
54
55
    /**
56
     * @var
57
     */
58
    protected $endpoint;
59
60
    /**
61
     * @var
62
     */
63
    protected $bucket;
64
65
    /**
66
     * @var
67
     */
68
    protected $isCName;
69
70
    /**
71
     * @var array
72
     */
73
    protected $buckets;
74
75
    /**
76
     * @var OssClient
77
     */
78
    protected $client;
79
80
    /**
81
     * @var array|mixed[]
82
     */
83
    protected $params;
84
85
    /**
86
     * @var bool
87
     */
88
    protected $useSSL = false;
89
90
    /**
91
     * OssAdapter constructor.
92
     *
93
     * @param       $accessKeyId
94
     * @param       $accessKeySecret
95
     * @param       $endpoint
96
     * @param       $bucket
97
     * @param bool  $isCName
98
     * @param       $prefix
99
     * @param array $buckets
100
     * @param mixed ...$params
101
     *
102
     * @throws OssException
103
     */
104
    public function __construct($accessKeyId, $accessKeySecret, $endpoint, $bucket, $isCName = false, $prefix = '', $buckets = [], ...$params)
105
    {
106
        $this->accessKeyId = $accessKeyId;
107
        $this->accessKeySecret = $accessKeySecret;
108
        $this->endpoint = $endpoint;
109
        $this->bucket = $bucket;
110
        $this->isCName = $isCName;
111
        $this->setPathPrefix($prefix);
112
        $this->buckets = $buckets;
113
        $this->params = $params;
114
        $this->initClient();
115
        $this->checkEndpoint();
116
    }
117
118
    /**
119
     * 调用不同的桶配置.
120
     *
121
     * @param $bucket
122
     *
123
     * @return $this
124
     *
125
     * @throws OssException
126
     */
127
    public function bucket($bucket)
128
    {
129
        if (!isset($this->buckets[$bucket])) {
130
            throw new \Exception('bucket is not exist.');
131
        }
132
        $bucketConfig = $this->buckets[$bucket];
133
134
        $this->accessKeyId = $bucketConfig['access_key'];
135
        $this->accessKeySecret = $bucketConfig['secret_key'];
136
        $this->endpoint = $bucketConfig['endpoint'];
137
        $this->bucket = $bucketConfig['bucket'];
138
        $this->isCName = $bucketConfig['isCName'];
139
140
        $this->initClient();
141
        $this->checkEndpoint();
142
143
        return $this;
144
    }
145
146
    /**
147
     * init oss client.
148
     *
149
     * @throws OssException
150
     */
151
    protected function initClient()
152
    {
153
        if (empty($this->client)) {
154
            $this->client = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint, $this->isCName, ...$this->params);
0 ignored issues
show
Bug introduced by
$this->params is expanded, but the parameter $securityToken of OSS\OssClient::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

154
            $this->client = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint, $this->isCName, /** @scrutinizer ignore-type */ ...$this->params);
Loading history...
155
        }
156
    }
157
158
    /**
159
     * get ali sdk kernel class.
160
     *
161
     * @return OssClient
162
     */
163
    public function getClient()
164
    {
165
        return $this->client;
166
    }
167
168
    /**
169
     * oss 直传配置.
170
     *
171
     * @param string $prefix
172
     * @param null   $callBackUrl
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $callBackUrl is correct as it would always require null to be passed?
Loading history...
173
     * @param array  $customData
174
     * @param int    $expire
175
     * @param int    $contentLengthRangeValue
176
     * @param array  $systemData
177
     *
178
     * @return false|string
179
     *
180
     * @throws \Exception
181
     */
182
    public function signatureConfig($prefix = '', $callBackUrl = null, $customData = [], $expire = 30, $contentLengthRangeValue = 1048576000, $systemData = [])
183
    {
184
        if (!empty($prefix)) {
185
            $prefix = ltrim($prefix, '/');
186
        }
187
188
        // 系统参数
189
        $system = [];
190
        if (empty($systemData)) {
191
            $system = self::SYSTEM_FIELD;
192
        } else {
193
            foreach ($systemData as $key => $value) {
194
                if (!in_array($value, self::SYSTEM_FIELD)) {
195
                    throw new \InvalidArgumentException("Invalid oss system filed: ${value}");
196
                }
197
                $system[$key] = $value;
198
            }
199
        }
200
201
        // 自定义参数
202
        $callbackVar = [];
203
        $data = [];
204
        if (!empty($customData)) {
205
            foreach ($customData as $key => $value) {
206
                $callbackVar['x:'.$key] = $value;
207
                $data[$key] = '${x:'.$key.'}';
208
            }
209
        }
210
211
        $callbackParam = [
212
            'callbackUrl' => $callBackUrl,
213
            'callbackBody' => urldecode(http_build_query(array_merge($system, $data))),
214
            'callbackBodyType' => 'application/x-www-form-urlencoded',
215
        ];
216
        $callbackString = json_encode($callbackParam);
217
        $base64CallbackBody = base64_encode($callbackString);
218
219
        $now = time();
220
        $end = $now + $expire;
221
        $expiration = $this->gmt_iso8601($end);
222
223
        // 最大文件大小.用户可以自己设置
224
        $condition = [
225
            0 => 'content-length-range',
226
            1 => 0,
227
            2 => $contentLengthRangeValue,
228
        ];
229
        $conditions[] = $condition;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$conditions was never initialized. Although not strictly required by PHP, it is generally a good practice to add $conditions = array(); before regardless.
Loading history...
230
231
        $start = [
232
            0 => 'starts-with',
233
            1 => '$key',
234
            2 => $prefix,
235
        ];
236
        $conditions[] = $start;
237
238
        $arr = [
239
            'expiration' => $expiration,
240
            'conditions' => $conditions,
241
        ];
242
        $policy = json_encode($arr);
243
        $base64Policy = base64_encode($policy);
244
        $stringToSign = $base64Policy;
245
        $signature = base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret, true));
246
247
        $response = [];
248
        $response['accessid'] = $this->accessKeyId;
249
        $response['host'] = $this->normalizeHost();
250
        $response['policy'] = $base64Policy;
251
        $response['signature'] = $signature;
252
        $response['expire'] = $end;
253
        $response['callback'] = $base64CallbackBody;
254
        $response['callback-var'] = $callbackVar;
255
        $response['dir'] = $prefix;  // 这个参数是设置用户上传文件时指定的前缀。
256
257
        return json_encode($response);
258
    }
259
260
    /**
261
     * sign url.
262
     *
263
     * @param       $path
264
     * @param       $timeout
265
     * @param array $options
266
     *
267
     * @return bool|string
268
     */
269
    public function signUrl($path, $timeout, array $options = [])
270
    {
271
        $path = $this->applyPathPrefix($path);
272
273
        try {
274
            $path = $this->client->signUrl($this->bucket, $path, $timeout, OssClient::OSS_HTTP_GET, $options);
275
        } catch (OssException $exception) {
276
            return false;
277
        }
278
279
        return $path;
280
    }
281
282
    /**
283
     * temporary file url.
284
     *
285
     * @param       $path
286
     * @param       $expiration
287
     * @param array $options
288
     *
289
     * @return bool|string
290
     */
291
    public function getTemporaryUrl($path, $expiration, array $options = [])
292
    {
293
        return $this->signUrl($path, Carbon::now()->diffInSeconds($expiration), $options);
294
    }
295
296
    /**
297
     * write a file.
298
     *
299
     * @param string $path
300
     * @param string $contents
301
     * @param Config $config
302
     *
303
     * @return array|bool|false
304
     */
305
    public function write($path, $contents, Config $config)
306
    {
307
        $path = $this->applyPathPrefix($path);
308
309
        $options = [];
310
311
        if ($config->has('options')) {
312
            $options = $config->get('options');
313
        }
314
315
        $this->client->putObject($this->bucket, $path, $contents, $options);
316
317
        return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::write() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
318
    }
319
320
    /**
321
     * Write a new file using a stream.
322
     *
323
     * @param string   $path
324
     * @param resource $resource
325
     * @param Config   $config
326
     *
327
     * @return array|bool|false
328
     */
329
    public function writeStream($path, $resource, Config $config)
330
    {
331
        $contents = stream_get_contents($resource);
332
333
        return $this->write($path, $contents, $config);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->write($path, $contents, $config) returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::writeStream() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
334
    }
335
336
    /**
337
     * Update a file.
338
     *
339
     * @param string $path
340
     * @param string $contents
341
     * @param Config $config
342
     *
343
     * @return array|bool|false
344
     */
345
    public function update($path, $contents, Config $config)
346
    {
347
        return $this->write($path, $contents, $config);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->write($path, $contents, $config) returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::update() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
348
    }
349
350
    /**
351
     * Update a file using a stream.
352
     *
353
     * @param string   $path
354
     * @param resource $resource
355
     * @param Config   $config
356
     *
357
     * @return array|bool|false
358
     */
359
    public function updateStream($path, $resource, Config $config)
360
    {
361
        return $this->writeStream($path, $resource, $config);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->writeStrea...th, $resource, $config) returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::updateStream() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
362
    }
363
364
    /**
365
     * rename a file.
366
     *
367
     * @param string $path
368
     * @param string $newpath
369
     *
370
     * @return bool
371
     *
372
     * @throws OssException
373
     */
374
    public function rename($path, $newpath)
375
    {
376
        if (!$this->copy($path, $newpath)) {
377
            return false;
378
        }
379
380
        return $this->delete($path);
381
    }
382
383
    /**
384
     * copy a file.
385
     *
386
     * @param string $path
387
     * @param string $newpath
388
     *
389
     * @return bool
390
     */
391
    public function copy($path, $newpath)
392
    {
393
        $path = $this->applyPathPrefix($path);
394
        $newpath = $this->applyPathPrefix($newpath);
395
396
        try {
397
            $this->client->copyObject($this->bucket, $path, $this->bucket, $newpath);
398
        } catch (OssException $exception) {
399
            return false;
400
        }
401
402
        return true;
403
    }
404
405
    /**
406
     * delete a file.
407
     *
408
     * @param string $path
409
     *
410
     * @return bool
411
     *
412
     * @throws OssException
413
     */
414
    public function delete($path)
415
    {
416
        $path = $this->applyPathPrefix($path);
417
418
        try {
419
            $this->client->deleteObject($this->bucket, $path);
420
        } catch (OssException $ossException) {
421
            return false;
422
        }
423
424
        return !$this->has($path);
425
    }
426
427
    /**
428
     * Delete a directory.
429
     *
430
     * @param string $dirname
431
     *
432
     * @return bool
433
     *
434
     * @throws OssException
435
     */
436
    public function deleteDir($dirname)
437
    {
438
        $fileList = $this->listContents($dirname, true);
439
        foreach ($fileList as $file) {
440
            $this->delete($file['path']);
441
        }
442
443
        return !$this->has($dirname);
444
    }
445
446
    /**
447
     * create a directory.
448
     *
449
     * @param string $dirname
450
     * @param Config $config
451
     *
452
     * @return bool
453
     */
454
    public function createDir($dirname, Config $config)
455
    {
456
        $defaultFile = trim($dirname, '/').'/oss.txt';
457
458
        return $this->write($defaultFile, '当虚拟目录下有其他文件时,可删除此文件~', $config);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->write($def... returns the type true which is incompatible with the return type mandated by League\Flysystem\AdapterInterface::createDir() of array|false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
459
    }
460
461
    /**
462
     * visibility.
463
     *
464
     * @param string $path
465
     * @param string $visibility
466
     *
467
     * @return array|bool|false
468
     */
469
    public function setVisibility($path, $visibility)
470
    {
471
        $object = $this->applyPathPrefix($path);
472
        $acl = (AdapterInterface::VISIBILITY_PUBLIC === $visibility) ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE;
473
474
        try {
475
            $this->client->putObjectAcl($this->bucket, $object, $acl);
476
        } catch (OssException $exception) {
477
            return false;
478
        }
479
480
        return compact('visibility');
481
    }
482
483
    /**
484
     * Check whether a file exists.
485
     *
486
     * @param string $path
487
     *
488
     * @return array|bool|null
489
     */
490
    public function has($path)
491
    {
492
        $path = $this->applyPathPrefix($path);
493
494
        return $this->client->doesObjectExist($this->bucket, $path);
495
    }
496
497
    /**
498
     * Get resource url.
499
     *
500
     * @param string $path
501
     *
502
     * @return string
503
     */
504
    public function getUrl($path)
505
    {
506
        $path = $this->applyPathPrefix($path);
507
508
        return $this->normalizeHost().ltrim($path, '/');
509
    }
510
511
    /**
512
     * read a file.
513
     *
514
     * @param string $path
515
     *
516
     * @return array|bool|false
517
     */
518
    public function read($path)
519
    {
520
        try {
521
            $contents = $this->getObject($path);
522
        } catch (OssException $exception) {
523
            return false;
524
        }
525
526
        return compact('contents', 'path');
527
    }
528
529
    /**
530
     * read a file stream.
531
     *
532
     * @param string $path
533
     *
534
     * @return array|bool|false
535
     */
536
    public function readStream($path)
537
    {
538
        try {
539
            $stream = $this->getObject($path);
540
        } catch (OssException $exception) {
541
            return false;
542
        }
543
544
        return compact('stream', 'path');
545
    }
546
547
    /**
548
     * Lists all files in the directory.
549
     *
550
     * @param string $directory
551
     * @param bool   $recursive
552
     *
553
     * @return array
554
     *
555
     * @throws OssException
556
     */
557
    public function listContents($directory = '', $recursive = false)
558
    {
559
        $list = [];
560
561
        $result = $this->listDirObjects($directory, true);
562
563
        if (!empty($result['objects'])) {
564
            foreach ($result['objects'] as $files) {
565
                if (!$fileInfo = $this->normalizeFileInfo($files)) {
566
                    continue;
567
                }
568
569
                $list[] = $fileInfo;
570
            }
571
        }
572
573
        return $list;
574
    }
575
576
    /**
577
     * get meta data.
578
     *
579
     * @param string $path
580
     *
581
     * @return array|bool|false
582
     */
583
    public function getMetadata($path)
584
    {
585
        $path = $this->applyPathPrefix($path);
586
587
        try {
588
            $metadata = $this->client->getObjectMeta($this->bucket, $path);
589
        } catch (OssException $exception) {
590
            return false;
591
        }
592
593
        return $metadata;
594
    }
595
596
    /**
597
     * get the size of file.
598
     *
599
     * @param string $path
600
     *
601
     * @return array|false
602
     */
603
    public function getSize($path)
604
    {
605
        return $this->normalizeFileInfo(['Key' => $path]);
606
    }
607
608
    /**
609
     * get mime type.
610
     *
611
     * @param string $path
612
     *
613
     * @return array|false
614
     */
615
    public function getMimetype($path)
616
    {
617
        return $this->normalizeFileInfo(['Key' => $path]);
618
    }
619
620
    /**
621
     * get timestamp.
622
     *
623
     * @param string $path
624
     *
625
     * @return array|false
626
     */
627
    public function getTimestamp($path)
628
    {
629
        return $this->normalizeFileInfo(['Key' => $path]);
630
    }
631
632
    /**
633
     * normalize Host.
634
     *
635
     * @return string
636
     */
637
    protected function normalizeHost()
638
    {
639
        if ($this->isCName) {
640
            $domain = $this->endpoint;
641
        } else {
642
            $domain = $this->bucket.'.'.$this->endpoint;
643
        }
644
645
        if ($this->useSSL) {
646
            $domain = "https://{$domain}";
647
        } else {
648
            $domain = "http://{$domain}";
649
        }
650
651
        return rtrim($domain, '/').'/';
652
    }
653
654
    /**
655
     * Check the endpoint to see if SSL can be used.
656
     */
657
    protected function checkEndpoint()
658
    {
659
        if (0 === strpos($this->endpoint, 'http://')) {
660
            $this->endpoint = substr($this->endpoint, strlen('http://'));
661
            $this->useSSL = false;
662
        } elseif (0 === strpos($this->endpoint, 'https://')) {
663
            $this->endpoint = substr($this->endpoint, strlen('https://'));
664
            $this->useSSL = true;
665
        }
666
    }
667
668
    /**
669
     * Read an object from the OssClient.
670
     *
671
     * @param $path
672
     *
673
     * @return string
674
     */
675
    protected function getObject($path)
676
    {
677
        $path = $this->applyPathPrefix($path);
678
679
        return $this->client->getObject($this->bucket, $path);
680
    }
681
682
    /**
683
     * File list core method.
684
     *
685
     * @param string $dirname
686
     * @param bool   $recursive
687
     *
688
     * @return array
689
     *
690
     * @throws OssException
691
     */
692
    public function listDirObjects($dirname = '', $recursive = false)
693
    {
694
        $delimiter = '/';
695
        $nextMarker = '';
696
        $maxkeys = 1000;
697
698
        $result = [];
699
700
        while (true) {
701
            $options = [
702
                'delimiter' => $delimiter,
703
                'prefix' => $dirname,
704
                'max-keys' => $maxkeys,
705
                'marker' => $nextMarker,
706
            ];
707
708
            try {
709
                $listObjectInfo = $this->client->listObjects($this->bucket, $options);
710
            } catch (OssException $exception) {
711
                throw $exception;
712
            }
713
714
            $nextMarker = $listObjectInfo->getNextMarker();
715
            $objectList = $listObjectInfo->getObjectList();
716
            $prefixList = $listObjectInfo->getPrefixList();
717
718
            if (!empty($objectList)) {
719
                foreach ($objectList as $objectInfo) {
720
                    $object['Prefix'] = $dirname;
721
                    $object['Key'] = $objectInfo->getKey();
722
                    $object['LastModified'] = $objectInfo->getLastModified();
723
                    $object['eTag'] = $objectInfo->getETag();
724
                    $object['Type'] = $objectInfo->getType();
725
                    $object['Size'] = $objectInfo->getSize();
726
                    $object['StorageClass'] = $objectInfo->getStorageClass();
727
                    $result['objects'][] = $object;
728
                }
729
            } else {
730
                $result['objects'] = [];
731
            }
732
733
            if (!empty($prefixList)) {
734
                foreach ($prefixList as $prefixInfo) {
735
                    $result['prefix'][] = $prefixInfo->getPrefix();
736
                }
737
            } else {
738
                $result['prefix'] = [];
739
            }
740
741
            // Recursive directory
742
            if ($recursive) {
743
                foreach ($result['prefix'] as $prefix) {
744
                    $next = $this->listDirObjects($prefix, $recursive);
745
                    $result['objects'] = array_merge($result['objects'], $next['objects']);
746
                }
747
            }
748
749
            if ('' === $nextMarker) {
750
                break;
751
            }
752
        }
753
754
        return $result;
755
    }
756
757
    /**
758
     * normalize file info.
759
     *
760
     * @param array $stats
761
     *
762
     * @return array
763
     */
764
    protected function normalizeFileInfo(array $stats)
765
    {
766
        $filePath = ltrim($stats['Key'], '/');
767
768
        $meta = $this->getMetadata($filePath) ?? [];
769
770
        if (empty($meta)) {
771
            return [];
772
        }
773
774
        return [
775
            'type' => 'file',
776
            'mimetype' => $meta['content-type'],
777
            'path' => $filePath,
778
            'timestamp' => $meta['info']['filetime'],
779
            'size' => $meta['content-length'],
780
        ];
781
    }
782
}
783