Passed
Pull Request — master (#46)
by
unknown
02:33
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
     *
177
     * @return false|string
178
     *
179
     * @throws \Exception
180
     */
181
    public function signatureConfig($prefix = '', $callBackUrl = null, $customData = [], $expire = 30, $contentLengthRangeValue = 1048576000, $systemData = null)
182
    {
183
        if (!empty($prefix)) {
184
            $prefix = ltrim($prefix, '/');
185
        }
186
187
        // 系统参数
188
        $system = [];
189
        if (empty($systemData)) {
190
            $system = self::SYSTEM_FIELD;
191
        } else {
192
            foreach ($systemData as $key => $value) {
193
                if (!in_array($value, self::SYSTEM_FIELD)) {
194
                    throw new \InvalidArgumentException("Invalid oss system filed: ${value}");
195
                }
196
                $system[$key] = $value;
197
            }
198
        }
199
200
        // 自定义参数
201
        $callbackVar = [];
202
        $data = [];
203
        if (!empty($customData)) {
204
            foreach ($customData as $key => $value) {
205
                $callbackVar['x:'.$key] = $value;
206
                $data[$key] = '${x:'.$key.'}';
207
            }
208
        }
209
210
        $callbackParam = [
211
            'callbackUrl' => $callBackUrl,
212
            'callbackBody' => urldecode(http_build_query(array_merge($system, $data))),
213
            'callbackBodyType' => 'application/x-www-form-urlencoded',
214
        ];
215
        $callbackString = json_encode($callbackParam);
216
        $base64CallbackBody = base64_encode($callbackString);
217
218
        $now = time();
219
        $end = $now + $expire;
220
        $expiration = $this->gmt_iso8601($end);
221
222
        // 最大文件大小.用户可以自己设置
223
        $condition = [
224
            0 => 'content-length-range',
225
            1 => 0,
226
            2 => $contentLengthRangeValue,
227
        ];
228
        $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...
229
230
        $start = [
231
            0 => 'starts-with',
232
            1 => '$key',
233
            2 => $prefix,
234
        ];
235
        $conditions[] = $start;
236
237
        $arr = [
238
            'expiration' => $expiration,
239
            'conditions' => $conditions,
240
        ];
241
        $policy = json_encode($arr);
242
        $base64Policy = base64_encode($policy);
243
        $stringToSign = $base64Policy;
244
        $signature = base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret, true));
245
246
        $response = [];
247
        $response['accessid'] = $this->accessKeyId;
248
        $response['host'] = $this->normalizeHost();
249
        $response['policy'] = $base64Policy;
250
        $response['signature'] = $signature;
251
        $response['expire'] = $end;
252
        $response['callback'] = $base64CallbackBody;
253
        $response['callback-var'] = $callbackVar;
254
        $response['dir'] = $prefix;  // 这个参数是设置用户上传文件时指定的前缀。
255
256
        return json_encode($response);
257
    }
258
259
    /**
260
     * sign url.
261
     *
262
     * @param       $path
263
     * @param       $timeout
264
     * @param array $options
265
     *
266
     * @return bool|string
267
     */
268
    public function signUrl($path, $timeout, array $options = [])
269
    {
270
        $path = $this->applyPathPrefix($path);
271
272
        try {
273
            $path = $this->client->signUrl($this->bucket, $path, $timeout, OssClient::OSS_HTTP_GET, $options);
274
        } catch (OssException $exception) {
275
            return false;
276
        }
277
278
        return $path;
279
    }
280
281
    /**
282
     * temporary file url.
283
     *
284
     * @param       $path
285
     * @param       $expiration
286
     * @param array $options
287
     *
288
     * @return bool|string
289
     */
290
    public function getTemporaryUrl($path, $expiration, array $options = [])
291
    {
292
        return $this->signUrl($path, Carbon::now()->diffInSeconds($expiration), $options);
293
    }
294
295
    /**
296
     * write a file.
297
     *
298
     * @param string $path
299
     * @param string $contents
300
     * @param Config $config
301
     *
302
     * @return array|bool|false
303
     */
304
    public function write($path, $contents, Config $config)
305
    {
306
        $path = $this->applyPathPrefix($path);
307
308
        $options = [];
309
310
        if ($config->has('options')) {
311
            $options = $config->get('options');
312
        }
313
314
        $this->client->putObject($this->bucket, $path, $contents, $options);
315
316
        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...
317
    }
318
319
    /**
320
     * Write a new file using a stream.
321
     *
322
     * @param string   $path
323
     * @param resource $resource
324
     * @param Config   $config
325
     *
326
     * @return array|bool|false
327
     */
328
    public function writeStream($path, $resource, Config $config)
329
    {
330
        $contents = stream_get_contents($resource);
331
332
        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...
333
    }
334
335
    /**
336
     * Update a file.
337
     *
338
     * @param string $path
339
     * @param string $contents
340
     * @param Config $config
341
     *
342
     * @return array|bool|false
343
     */
344
    public function update($path, $contents, Config $config)
345
    {
346
        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...
347
    }
348
349
    /**
350
     * Update a file using a stream.
351
     *
352
     * @param string   $path
353
     * @param resource $resource
354
     * @param Config   $config
355
     *
356
     * @return array|bool|false
357
     */
358
    public function updateStream($path, $resource, Config $config)
359
    {
360
        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...
361
    }
362
363
    /**
364
     * rename a file.
365
     *
366
     * @param string $path
367
     * @param string $newpath
368
     *
369
     * @return bool
370
     *
371
     * @throws OssException
372
     */
373
    public function rename($path, $newpath)
374
    {
375
        if (!$this->copy($path, $newpath)) {
376
            return false;
377
        }
378
379
        return $this->delete($path);
380
    }
381
382
    /**
383
     * copy a file.
384
     *
385
     * @param string $path
386
     * @param string $newpath
387
     *
388
     * @return bool
389
     */
390
    public function copy($path, $newpath)
391
    {
392
        $path = $this->applyPathPrefix($path);
393
        $newpath = $this->applyPathPrefix($newpath);
394
395
        try {
396
            $this->client->copyObject($this->bucket, $path, $this->bucket, $newpath);
397
        } catch (OssException $exception) {
398
            return false;
399
        }
400
401
        return true;
402
    }
403
404
    /**
405
     * delete a file.
406
     *
407
     * @param string $path
408
     *
409
     * @return bool
410
     *
411
     * @throws OssException
412
     */
413
    public function delete($path)
414
    {
415
        $path = $this->applyPathPrefix($path);
416
417
        try {
418
            $this->client->deleteObject($this->bucket, $path);
419
        } catch (OssException $ossException) {
420
            return false;
421
        }
422
423
        return !$this->has($path);
424
    }
425
426
    /**
427
     * Delete a directory.
428
     *
429
     * @param string $dirname
430
     *
431
     * @return bool
432
     *
433
     * @throws OssException
434
     */
435
    public function deleteDir($dirname)
436
    {
437
        $fileList = $this->listContents($dirname, true);
438
        foreach ($fileList as $file) {
439
            $this->delete($file['path']);
440
        }
441
442
        return !$this->has($dirname);
443
    }
444
445
    /**
446
     * create a directory.
447
     *
448
     * @param string $dirname
449
     * @param Config $config
450
     *
451
     * @return bool
452
     */
453
    public function createDir($dirname, Config $config)
454
    {
455
        $defaultFile = trim($dirname, '/').'/oss.txt';
456
457
        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...
458
    }
459
460
    /**
461
     * visibility.
462
     *
463
     * @param string $path
464
     * @param string $visibility
465
     *
466
     * @return array|bool|false
467
     */
468
    public function setVisibility($path, $visibility)
469
    {
470
        $object = $this->applyPathPrefix($path);
471
        $acl = (AdapterInterface::VISIBILITY_PUBLIC === $visibility) ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE;
472
473
        try {
474
            $this->client->putObjectAcl($this->bucket, $object, $acl);
475
        } catch (OssException $exception) {
476
            return false;
477
        }
478
479
        return compact('visibility');
480
    }
481
482
    /**
483
     * Check whether a file exists.
484
     *
485
     * @param string $path
486
     *
487
     * @return array|bool|null
488
     */
489
    public function has($path)
490
    {
491
        $path = $this->applyPathPrefix($path);
492
493
        return $this->client->doesObjectExist($this->bucket, $path);
494
    }
495
496
    /**
497
     * Get resource url.
498
     *
499
     * @param string $path
500
     *
501
     * @return string
502
     */
503
    public function getUrl($path)
504
    {
505
        $path = $this->applyPathPrefix($path);
506
507
        return $this->normalizeHost().ltrim($path, '/');
508
    }
509
510
    /**
511
     * read a file.
512
     *
513
     * @param string $path
514
     *
515
     * @return array|bool|false
516
     */
517
    public function read($path)
518
    {
519
        try {
520
            $contents = $this->getObject($path);
521
        } catch (OssException $exception) {
522
            return false;
523
        }
524
525
        return compact('contents', 'path');
526
    }
527
528
    /**
529
     * read a file stream.
530
     *
531
     * @param string $path
532
     *
533
     * @return array|bool|false
534
     */
535
    public function readStream($path)
536
    {
537
        try {
538
            $stream = $this->getObject($path);
539
        } catch (OssException $exception) {
540
            return false;
541
        }
542
543
        return compact('stream', 'path');
544
    }
545
546
    /**
547
     * Lists all files in the directory.
548
     *
549
     * @param string $directory
550
     * @param bool   $recursive
551
     *
552
     * @return array
553
     *
554
     * @throws OssException
555
     */
556
    public function listContents($directory = '', $recursive = false)
557
    {
558
        $list = [];
559
560
        $result = $this->listDirObjects($directory, true);
561
562
        if (!empty($result['objects'])) {
563
            foreach ($result['objects'] as $files) {
564
                if (!$fileInfo = $this->normalizeFileInfo($files)) {
565
                    continue;
566
                }
567
568
                $list[] = $fileInfo;
569
            }
570
        }
571
572
        return $list;
573
    }
574
575
    /**
576
     * get meta data.
577
     *
578
     * @param string $path
579
     *
580
     * @return array|bool|false
581
     */
582
    public function getMetadata($path)
583
    {
584
        $path = $this->applyPathPrefix($path);
585
586
        try {
587
            $metadata = $this->client->getObjectMeta($this->bucket, $path);
588
        } catch (OssException $exception) {
589
            return false;
590
        }
591
592
        return $metadata;
593
    }
594
595
    /**
596
     * get the size of file.
597
     *
598
     * @param string $path
599
     *
600
     * @return array|false
601
     */
602
    public function getSize($path)
603
    {
604
        return $this->normalizeFileInfo(['Key' => $path]);
605
    }
606
607
    /**
608
     * get mime type.
609
     *
610
     * @param string $path
611
     *
612
     * @return array|false
613
     */
614
    public function getMimetype($path)
615
    {
616
        return $this->normalizeFileInfo(['Key' => $path]);
617
    }
618
619
    /**
620
     * get timestamp.
621
     *
622
     * @param string $path
623
     *
624
     * @return array|false
625
     */
626
    public function getTimestamp($path)
627
    {
628
        return $this->normalizeFileInfo(['Key' => $path]);
629
    }
630
631
    /**
632
     * normalize Host.
633
     *
634
     * @return string
635
     */
636
    protected function normalizeHost()
637
    {
638
        if ($this->isCName) {
639
            $domain = $this->endpoint;
640
        } else {
641
            $domain = $this->bucket.'.'.$this->endpoint;
642
        }
643
644
        if ($this->useSSL) {
645
            $domain = "https://{$domain}";
646
        } else {
647
            $domain = "http://{$domain}";
648
        }
649
650
        return rtrim($domain, '/').'/';
651
    }
652
653
    /**
654
     * Check the endpoint to see if SSL can be used.
655
     */
656
    protected function checkEndpoint()
657
    {
658
        if (0 === strpos($this->endpoint, 'http://')) {
659
            $this->endpoint = substr($this->endpoint, strlen('http://'));
660
            $this->useSSL = false;
661
        } elseif (0 === strpos($this->endpoint, 'https://')) {
662
            $this->endpoint = substr($this->endpoint, strlen('https://'));
663
            $this->useSSL = true;
664
        }
665
    }
666
667
    /**
668
     * Read an object from the OssClient.
669
     *
670
     * @param $path
671
     *
672
     * @return string
673
     */
674
    protected function getObject($path)
675
    {
676
        $path = $this->applyPathPrefix($path);
677
678
        return $this->client->getObject($this->bucket, $path);
679
    }
680
681
    /**
682
     * File list core method.
683
     *
684
     * @param string $dirname
685
     * @param bool   $recursive
686
     *
687
     * @return array
688
     *
689
     * @throws OssException
690
     */
691
    public function listDirObjects($dirname = '', $recursive = false)
692
    {
693
        $delimiter = '/';
694
        $nextMarker = '';
695
        $maxkeys = 1000;
696
697
        $result = [];
698
699
        while (true) {
700
            $options = [
701
                'delimiter' => $delimiter,
702
                'prefix' => $dirname,
703
                'max-keys' => $maxkeys,
704
                'marker' => $nextMarker,
705
            ];
706
707
            try {
708
                $listObjectInfo = $this->client->listObjects($this->bucket, $options);
709
            } catch (OssException $exception) {
710
                throw $exception;
711
            }
712
713
            $nextMarker = $listObjectInfo->getNextMarker();
714
            $objectList = $listObjectInfo->getObjectList();
715
            $prefixList = $listObjectInfo->getPrefixList();
716
717
            if (!empty($objectList)) {
718
                foreach ($objectList as $objectInfo) {
719
                    $object['Prefix'] = $dirname;
720
                    $object['Key'] = $objectInfo->getKey();
721
                    $object['LastModified'] = $objectInfo->getLastModified();
722
                    $object['eTag'] = $objectInfo->getETag();
723
                    $object['Type'] = $objectInfo->getType();
724
                    $object['Size'] = $objectInfo->getSize();
725
                    $object['StorageClass'] = $objectInfo->getStorageClass();
726
                    $result['objects'][] = $object;
727
                }
728
            } else {
729
                $result['objects'] = [];
730
            }
731
732
            if (!empty($prefixList)) {
733
                foreach ($prefixList as $prefixInfo) {
734
                    $result['prefix'][] = $prefixInfo->getPrefix();
735
                }
736
            } else {
737
                $result['prefix'] = [];
738
            }
739
740
            // Recursive directory
741
            if ($recursive) {
742
                foreach ($result['prefix'] as $prefix) {
743
                    $next = $this->listDirObjects($prefix, $recursive);
744
                    $result['objects'] = array_merge($result['objects'], $next['objects']);
745
                }
746
            }
747
748
            if ('' === $nextMarker) {
749
                break;
750
            }
751
        }
752
753
        return $result;
754
    }
755
756
    /**
757
     * normalize file info.
758
     *
759
     * @param array $stats
760
     *
761
     * @return array
762
     */
763
    protected function normalizeFileInfo(array $stats)
764
    {
765
        $filePath = ltrim($stats['Key'], '/');
766
767
        $meta = $this->getMetadata($filePath) ?? [];
768
769
        if (empty($meta)) {
770
            return [];
771
        }
772
773
        return [
774
            'type' => 'file',
775
            'mimetype' => $meta['content-type'],
776
            'path' => $filePath,
777
            'timestamp' => $meta['info']['filetime'],
778
            'size' => $meta['content-length'],
779
        ];
780
    }
781
}
782