Passed
Push — master ( 9942bf...774758 )
by Luo
01:01 queued 10s
created

OssAdapter::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 5
Bugs 3 Features 0
Metric Value
cc 1
eloc 10
c 5
b 3
f 0
nc 1
nop 8
dl 0
loc 12
ccs 0
cts 12
cp 0
crap 2
rs 9.9332

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
        $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

153
        $this->client = new OssClient($this->accessKeyId, $this->accessKeySecret, $this->endpoint, $this->isCName, /** @scrutinizer ignore-type */ ...$this->params);
Loading history...
154
    }
155
156
    /**
157
     * get ali sdk kernel class.
158
     *
159
     * @return OssClient
160
     */
161
    public function getClient()
162
    {
163
        return $this->client;
164
    }
165
166
    /**
167
     * oss 直传配置.
168
     *
169
     * @param string $prefix
170
     * @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...
171
     * @param array  $customData
172
     * @param int    $expire
173
     * @param int    $contentLengthRangeValue
174
     * @param array  $systemData
175
     *
176
     * @return false|string
177
     *
178
     * @throws \Exception
179
     */
180
    public function signatureConfig($prefix = '', $callBackUrl = null, $customData = [], $expire = 30, $contentLengthRangeValue = 1048576000, $systemData = [])
181
    {
182
        if (!empty($prefix)) {
183
            $prefix = ltrim($prefix, '/');
184
        }
185
186
        // 系统参数
187
        $system = [];
188
        if (empty($systemData)) {
189
            $system = self::SYSTEM_FIELD;
190
        } else {
191
            foreach ($systemData as $key => $value) {
192
                if (!in_array($value, self::SYSTEM_FIELD)) {
193
                    throw new \InvalidArgumentException("Invalid oss system filed: ${value}");
194
                }
195
                $system[$key] = $value;
196
            }
197
        }
198
199
        // 自定义参数
200
        $callbackVar = [];
201
        $data = [];
202
        if (!empty($customData)) {
203
            foreach ($customData as $key => $value) {
204
                $callbackVar['x:'.$key] = $value;
205
                $data[$key] = '${x:'.$key.'}';
206
            }
207
        }
208
209
        $callbackParam = [
210
            'callbackUrl' => $callBackUrl,
211
            'callbackBody' => urldecode(http_build_query(array_merge($system, $data))),
212
            'callbackBodyType' => 'application/x-www-form-urlencoded',
213
        ];
214
        $callbackString = json_encode($callbackParam);
215
        $base64CallbackBody = base64_encode($callbackString);
216
217
        $now = time();
218
        $end = $now + $expire;
219
        $expiration = $this->gmt_iso8601($end);
220
221
        // 最大文件大小.用户可以自己设置
222
        $condition = [
223
            0 => 'content-length-range',
224
            1 => 0,
225
            2 => $contentLengthRangeValue,
226
        ];
227
        $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...
228
229
        $start = [
230
            0 => 'starts-with',
231
            1 => '$key',
232
            2 => $prefix,
233
        ];
234
        $conditions[] = $start;
235
236
        $arr = [
237
            'expiration' => $expiration,
238
            'conditions' => $conditions,
239
        ];
240
        $policy = json_encode($arr);
241
        $base64Policy = base64_encode($policy);
242
        $stringToSign = $base64Policy;
243
        $signature = base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret, true));
244
245
        $response = [];
246
        $response['accessid'] = $this->accessKeyId;
247
        $response['host'] = $this->normalizeHost();
248
        $response['policy'] = $base64Policy;
249
        $response['signature'] = $signature;
250
        $response['expire'] = $end;
251
        $response['callback'] = $base64CallbackBody;
252
        $response['callback-var'] = $callbackVar;
253
        $response['dir'] = $prefix;  // 这个参数是设置用户上传文件时指定的前缀。
254
255
        return json_encode($response);
256
    }
257
258
    /**
259
     * sign url.
260
     *
261
     * @param $path
262
     * @param $timeout
263
     *
264
     * @return bool|string
265
     */
266
    public function signUrl($path, $timeout, array $options = [])
267
    {
268
        $path = $this->applyPathPrefix($path);
269
270
        try {
271
            $path = $this->client->signUrl($this->bucket, $path, $timeout, OssClient::OSS_HTTP_GET, $options);
272
        } catch (OssException $exception) {
273
            return false;
274
        }
275
276
        return $path;
277
    }
278
279
    /**
280
     * temporary file url.
281
     *
282
     * @param $path
283
     * @param $expiration
284
     *
285
     * @return bool|string
286
     */
287
    public function getTemporaryUrl($path, $expiration, array $options = [])
288
    {
289
        return $this->signUrl($path, Carbon::now()->diffInSeconds($expiration), $options);
290
    }
291
292
    /**
293
     * write a file.
294
     *
295
     * @param string $path
296
     * @param string $contents
297
     *
298
     * @return array|bool|false
299
     */
300
    public function write($path, $contents, Config $config)
301
    {
302
        $path = $this->applyPathPrefix($path);
303
304
        $options = [];
305
306
        if ($config->has('options')) {
307
            $options = $config->get('options');
308
        }
309
310
        $this->client->putObject($this->bucket, $path, $contents, $options);
311
312
        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...
313
    }
314
315
    /**
316
     * Write a new file using a stream.
317
     *
318
     * @param string   $path
319
     * @param resource $resource
320
     *
321
     * @return array|bool|false
322
     */
323
    public function writeStream($path, $resource, Config $config)
324
    {
325
        $contents = stream_get_contents($resource);
326
327
        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...
328
    }
329
330
    /**
331
     * Update a file.
332
     *
333
     * @param string $path
334
     * @param string $contents
335
     *
336
     * @return array|bool|false
337
     */
338
    public function update($path, $contents, Config $config)
339
    {
340
        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...
341
    }
342
343
    /**
344
     * Update a file using a stream.
345
     *
346
     * @param string   $path
347
     * @param resource $resource
348
     *
349
     * @return array|bool|false
350
     */
351
    public function updateStream($path, $resource, Config $config)
352
    {
353
        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...
354
    }
355
356
    /**
357
     * rename a file.
358
     *
359
     * @param string $path
360
     * @param string $newpath
361
     *
362
     * @return bool
363
     *
364
     * @throws OssException
365
     */
366
    public function rename($path, $newpath)
367
    {
368
        if (!$this->copy($path, $newpath)) {
369
            return false;
370
        }
371
372
        return $this->delete($path);
373
    }
374
375
    /**
376
     * copy a file.
377
     *
378
     * @param string $path
379
     * @param string $newpath
380
     *
381
     * @return bool
382
     */
383
    public function copy($path, $newpath)
384
    {
385
        $path = $this->applyPathPrefix($path);
386
        $newpath = $this->applyPathPrefix($newpath);
387
388
        try {
389
            $this->client->copyObject($this->bucket, $path, $this->bucket, $newpath);
390
        } catch (OssException $exception) {
391
            return false;
392
        }
393
394
        return true;
395
    }
396
397
    /**
398
     * delete a file.
399
     *
400
     * @param string $path
401
     *
402
     * @return bool
403
     *
404
     * @throws OssException
405
     */
406
    public function delete($path)
407
    {
408
        $path = $this->applyPathPrefix($path);
409
410
        try {
411
            $this->client->deleteObject($this->bucket, $path);
412
        } catch (OssException $ossException) {
413
            return false;
414
        }
415
416
        return !$this->has($path);
417
    }
418
419
    /**
420
     * Delete a directory.
421
     *
422
     * @param string $dirname
423
     *
424
     * @return bool
425
     *
426
     * @throws OssException
427
     */
428
    public function deleteDir($dirname)
429
    {
430
        $fileList = $this->listContents($dirname, true);
431
        foreach ($fileList as $file) {
432
            $this->delete($file['path']);
433
        }
434
435
        return !$this->has($dirname);
436
    }
437
438
    /**
439
     * create a directory.
440
     *
441
     * @param string $dirname
442
     *
443
     * @return bool
444
     */
445
    public function createDir($dirname, Config $config)
446
    {
447
        $defaultFile = trim($dirname, '/').'/oss.txt';
448
449
        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...
450
    }
451
452
    /**
453
     * visibility.
454
     *
455
     * @param string $path
456
     * @param string $visibility
457
     *
458
     * @return array|bool|false
459
     */
460
    public function setVisibility($path, $visibility)
461
    {
462
        $object = $this->applyPathPrefix($path);
463
        $acl = (AdapterInterface::VISIBILITY_PUBLIC === $visibility) ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE;
464
465
        try {
466
            $this->client->putObjectAcl($this->bucket, $object, $acl);
467
        } catch (OssException $exception) {
468
            return false;
469
        }
470
471
        return compact('visibility');
472
    }
473
474
    /**
475
     * Check whether a file exists.
476
     *
477
     * @param string $path
478
     *
479
     * @return array|bool|null
480
     */
481
    public function has($path)
482
    {
483
        $path = $this->applyPathPrefix($path);
484
485
        return $this->client->doesObjectExist($this->bucket, $path);
486
    }
487
488
    /**
489
     * Get resource url.
490
     *
491
     * @param string $path
492
     *
493
     * @return string
494
     */
495
    public function getUrl($path)
496
    {
497
        $path = $this->applyPathPrefix($path);
498
499
        return $this->normalizeHost().ltrim($path, '/');
500
    }
501
502
    /**
503
     * read a file.
504
     *
505
     * @param string $path
506
     *
507
     * @return array|bool|false
508
     */
509
    public function read($path)
510
    {
511
        try {
512
            $contents = $this->getObject($path);
513
        } catch (OssException $exception) {
514
            return false;
515
        }
516
517
        return compact('contents', 'path');
518
    }
519
520
    /**
521
     * read a file stream.
522
     *
523
     * @param string $path
524
     *
525
     * @return array|bool|false
526
     */
527
    public function readStream($path)
528
    {
529
        try {
530
            $stream = $this->getObject($path);
531
        } catch (OssException $exception) {
532
            return false;
533
        }
534
535
        return compact('stream', 'path');
536
    }
537
538
    /**
539
     * Lists all files in the directory.
540
     *
541
     * @param string $directory
542
     * @param bool   $recursive
543
     *
544
     * @return array
545
     *
546
     * @throws OssException
547
     */
548
    public function listContents($directory = '', $recursive = false)
549
    {
550
        $list = [];
551
        $directory = '/' == substr($directory, -1) ? $directory : $directory.'/';
552
        $result = $this->listDirObjects($directory, $recursive);
553
554
        if (!empty($result['objects'])) {
555
            foreach ($result['objects'] as $files) {
556
                if ('oss.txt' == substr($files['Key'], -7) || !$fileInfo = $this->normalizeFileInfo($files)) {
557
                    continue;
558
                }
559
                $list[] = $fileInfo;
560
            }
561
        }
562
563
        // prefix
564
        if (!empty($result['prefix'])) {
565
            foreach ($result['prefix'] as $dir) {
566
                $list[] = [
567
                    'type' => 'dir',
568
                    'path' => $dir,
569
                ];
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
     * @return array
761
     */
762
    protected function normalizeFileInfo(array $stats)
763
    {
764
        $filePath = ltrim($stats['Key'], '/');
765
766
        $meta = $this->getMetadata($filePath) ?? [];
767
768
        if (empty($meta)) {
769
            return [];
770
        }
771
772
        return [
773
            'type' => 'file',
774
            'mimetype' => $meta['content-type'],
775
            'path' => $filePath,
776
            'timestamp' => $meta['info']['filetime'],
777
            'size' => $meta['content-length'],
778
        ];
779
    }
780
}
781