Qiniu::getResumeUpload()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 3
dl 0
loc 3
rs 10
1
<?php
2
// +----------------------------------------------------------------------
3
// | date: 2015-09-12
4
// +----------------------------------------------------------------------
5
// | Upload.php: 七牛上传实现
6
// +----------------------------------------------------------------------
7
// | Author: tinymeng <[email protected]>
8
// +----------------------------------------------------------------------
9
namespace tinymeng\uploads\Gateways;
10
11
use Exception;
12
use Qiniu\Auth;
13
use Qiniu\Storage\ResumeUploader;
14
use Qiniu\Storage\UploadManager;
15
use Qiniu\Storage\BucketManager;
16
use Qiniu\Config AS QiniuConfig;
17
use InvalidArgumentException;
18
use tinymeng\uploads\Helper\MimeType;
19
use tinymeng\uploads\Connector\Gateway;
20
use tinymeng\uploads\Helper\PathLibrary;
21
use tinymeng\uploads\Helper\FileFunction;
22
use tinymeng\uploads\exception\TinymengException;
23
24
class Qiniu extends Gateway
25
{
26
    /**
27
     * Auth
28
     *
29
     * @var Auth
30
     */
31
    protected $auth;
32
    /**
33
     * token
34
     *
35
     * @var string
36
     */
37
    protected $token;
38
    /**
39
     * bucket
40
     *
41
     * @var
42
     */
43
    protected $bucket;
44
    /**
45
     * 七牛空间管理对象
46
     *
47
     * @var
48
     */
49
    protected $bucketManager;
50
    /**
51
     * 上传对象
52
     *
53
     * @var
54
     */
55
    protected $uploadManager;
56
    /**
57
     * 配置信息
58
     *
59
     * @var array
60
     */
61
    protected $config;
62
    /**
63
     * 构造方法
64
     *
65
     * @param array $config 配置信息
66
     * @author tinymeng <[email protected]>
67
     */
68
    public function __construct($config)
69
    {
70
        $baseConfig = [
71
            'domain'        => '',//你的七牛域名
72
            'access_key'    => '',//AccessKey
73
            'secret_key'    => '',//SecretKey
74
            'bucket'        => '',//Bucket名字
75
            'transport'     => 'http',//如果支持https,请填写https,如果不支持请填写http
76
        ];
77
        $this->config   = array_replace_recursive($baseConfig,$config);
78
        $this->bucket   = $this->config['bucket'];
79
        $this->auth     = new Auth($this->config['access_key'], $this->config['secret_key']);
80
        $this->token    = $this->auth->uploadToken($this->bucket);
81
        //设置路径前缀
82
        $this->setPathPrefix($this->config['transport'] . '://' . $this->config['domain']);
83
    }
84
    /**
85
     * 格式化路径
86
     *
87
     * @param $path
88
     * @return string
89
     */
90
    protected static function normalizerPath($path)
91
    {
92
        $path = ltrim(PathLibrary::normalizerPath($path), '/');
93
        return $path == '/' ? '' : $path;
94
    }
95
    /**
96
     * 获得七牛空间管理对象
97
     *
98
     * @return BucketManager
99
     * @author tinymeng <[email protected]>
100
     */
101
    protected function getBucketManager()
102
    {
103
        if (!$this->bucketManager) {
104
            $this->bucketManager = new BucketManager($this->auth);
105
        }
106
        return $this->bucketManager;
107
    }
108
    /**
109
     * 获得七牛上传对象
110
     *
111
     * @return UploadManager
112
     * @author tinymeng <[email protected]>
113
     */
114
    protected function getClient()
115
    {
116
        if (!$this->uploadManager) {
117
            $this->uploadManager = new UploadManager();
118
        }
119
        return $this->uploadManager;
120
    }
121
    /**
122
     * 获得 Qiniu 实例
123
     *
124
     * @return UploadManager
125
     */
126
    public function getInstance()
127
    {
128
        return $this->getClient();
129
    }
130
    /**
131
     * 获得二进制流上传对象
132
     *
133
     * @param string $key          上传文件名
134
     * @param resource $inputStream  上传二进制流
135
     * @param array $params       自定义变量
136
     * @return ResumeUploader
137
     * @author tinymeng <[email protected]>
138
     */
139
    protected function getResumeUpload($key, $inputStream, $params = null)
140
    {
141
        return new ResumeUploader( $this->token, $key, $inputStream, $this->getResourceSize($inputStream), $params, $config->get('mimetype'), (new QiniuConfig()) );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $config seems to be never defined.
Loading history...
142
    }
143
    /**
144
     * 获得文件大小
145
     *
146
     * @param $inputStream
147
     * @return int
148
     */
149
    protected function getResourceSize($inputStream)
150
    {
151
        $size = 0;
152
        $a = &$inputStream;
153
        while( !feof($a) ) {
154
            $str = fgets($a);
155
            $size += strlen($str);
156
        }
157
        fseek($inputStream, 0);
158
        return $size;
159
    }
160
    /**
161
     * 判断文件是否存在
162
     *
163
     * @param string $path
164
     * @return bool
165
     * @author tinymeng <[email protected]>
166
     */
167
    public function has($path)
168
    {
169
        $file_stat = $this->getMetadata($path);
170
        return !empty($file_stat) ? true : false;
171
    }
172
    /**
173
     * 读取文件
174
     *
175
     * @param string $path
176
     * @return array
177
     * @throws TinymengException
178
     * @author tinymeng <[email protected]>
179
     */
180
    public function read($path)
181
    {
182
        try {
183
            $url = $this->applyPathPrefix(static::normalizerPath($path));
184
            $contents = file_get_contents($url);
185
            if ($contents === false) {
186
                throw new TinymengException("文件读取失败: {$path}");
187
            }
188
            return ['contents' => $contents];
189
        } catch (Exception $e) {
190
            if ($e instanceof TinymengException) {
191
                throw $e;
192
            }
193
            throw new TinymengException($e->getMessage());
194
        }
195
    }
196
    /**
197
     * 获得文件流
198
     *
199
     * @param string $path
200
     * @return array
201
     * @throws TinymengException
202
     * @author tinymeng <[email protected]>
203
     */
204
    public function readStream($path)
205
    {
206
        try {
207
            $url = $this->applyPathPrefix(static::normalizerPath($path));
208
            $handle = fopen($url, 'r');
209
            if ($handle === false) {
210
                throw new TinymengException("文件流读取失败: {$path}");
211
            }
212
            return ['stream' => $handle];
213
        } catch (Exception $e) {
214
            if ($e instanceof TinymengException) {
215
                throw $e;
216
            }
217
            throw new TinymengException($e->getMessage());
218
        }
219
    }
220
    /**
221
     * 写入文件
222
     *
223
     * @param string $path
224
     * @param string $contents
225
     * @param array $option
226
     * @return array|bool|false
227
     * @throws TinymengException
228
     * @author tinymeng <[email protected]>
229
     */
230
    public function write($path, $contents, $option = [])
231
    {
232
        try {
233
            list($result, $error) = $this->getClient()->put($this->token, static::normalizerPath($path), $contents, $option);
234
            if ($error) {
235
                throw new TinymengException("文件写入失败: " . $error->message());
236
            }
237
            return $result !== null ? $result : true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result !== null ? $result : true also could return the type true which is incompatible with the return type mandated by tinymeng\uploads\Connect...tewayInterface::write() of array|false.
Loading history...
238
        } catch (Exception $e) {
239
            if ($e instanceof TinymengException) {
240
                throw $e;
241
            }
242
            throw new TinymengException($e->getMessage());
243
        }
244
    }
245
    /**
246
     * 写入文件流
247
     *
248
     * @param string $path
249
     * @param resource $resource
250
     * @param array $option
251
     * @return array|bool|false
252
     * @throws TinymengException
253
     */
254
    public function writeStream($path, $resource, $option = [])
255
    {
256
        try {
257
            //获得一个临时文件
258
            $tmpfname = FileFunction::getTmpFile();
259
            
260
            // 将资源流写入临时文件
261
            $contents = stream_get_contents($resource);
262
            file_put_contents($tmpfname, $contents);
263
            
264
            // 读取文件内容并上传
265
            $fileContent = file_get_contents($tmpfname);
266
            list($result, $error) = $this->getClient()->put($this->token, static::normalizerPath($path), $fileContent, $option);
267
            
268
            //删除临时文件
269
            FileFunction::deleteTmpFile($tmpfname);
270
            
271
            if ($error) {
272
                throw new TinymengException("文件流写入失败: " . $error->message());
273
            }
274
            return $result !== null ? $result : true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result !== null ? $result : true also could return the type true which is incompatible with the return type mandated by tinymeng\uploads\Connect...nterface::writeStream() of array|false.
Loading history...
275
        } catch (Exception $e) {
276
            if ($e instanceof TinymengException) {
277
                throw $e;
278
            }
279
            throw new TinymengException($e->getMessage());
280
        }
281
    }
282
    /**
283
     * Name: 上传文件
284
     * Author: Tinymeng <[email protected]>
285
     * @param $path
286
     * @param $tmpfname
287
     * @param array $option
288
     * @return array 返回文件信息,包含 url, etag, size 等
289
     * @throws TinymengException
290
     */
291
    public function uploadFile($path, $tmpfname, $option = [])
292
    {
293
        try {
294
            $path = static::normalizerPath($path);
295
            if (!file_exists($tmpfname)) {
296
                throw new TinymengException("文件不存在: {$tmpfname}");
297
            }
298
            
299
            // 读取文件内容
300
            $fileContent = file_get_contents($tmpfname);
301
            
302
            // 上传文件
303
            list($result, $error) = $this->getClient()->put($this->token, $path, $fileContent, $option);
304
            
305
            if ($error) {
306
                throw new TinymengException("文件上传失败: " . $error->message());
307
            }
308
            
309
            // 构建返回信息
310
            $fileInfo = [
311
                'success' => true,
312
                'path' => $path,
313
                'key' => $path,
314
                'etag' => isset($result['hash']) ? $result['hash'] : '',
315
                'size' => filesize($tmpfname),
316
            ];
317
            
318
            // 获取文件 URL
319
            try {
320
                $fileInfo['url'] = $this->getUrl($path, 0); // 0 表示永久 URL
321
            } catch (Exception $e) {
322
                // 如果获取 URL 失败,使用路径前缀构建 URL
323
                $fileInfo['url'] = $this->applyPathPrefix($path);
324
            }
325
            
326
            // 添加其他可能的响应信息
327
            if (isset($result['key'])) {
328
                $fileInfo['key'] = $result['key'];
329
            }
330
            
331
            return $fileInfo;
332
        } catch (Exception $e) {
333
            throw new TinymengException($e->getMessage());
334
        }
335
    }
336
    /**
337
     * 更新文件
338
     *
339
     * @param string $path
340
     * @param string $contents
341
     */
342
    public function update($path, $contents)
343
    {
344
        return $this->write($path, $contents);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->write($path, $contents) also could return the type boolean which is incompatible with the return type mandated by tinymeng\uploads\Connect...ewayInterface::update() of array|false.
Loading history...
345
    }
346
    /**
347
     * 更新文件流
348
     *
349
     * @param string $path
350
     * @param resource $resource
351
     * @param array $option
352
     * @return array|bool|false
353
     * @throws TinymengException
354
     */
355
    public function updateStream($path, $resource, $option = [])
356
    {
357
        return $this->writeStream($path, $resource, $option);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->writeStrea...th, $resource, $option) also could return the type boolean which is incompatible with the return type mandated by tinymeng\uploads\Connect...terface::updateStream() of array|false.
Loading history...
358
    }
359
    /**
360
     * 列出目录文件
361
     *
362
     * @param string $directory
363
     * @param bool|false $recursive
364
     * @return array
365
     * @throws TinymengException
366
     * @author tinymeng <[email protected]>
367
     */
368
    public function listContents($directory = '', $recursive = false)
0 ignored issues
show
Unused Code introduced by
The parameter $recursive is not used and could be removed. ( Ignorable by Annotation )

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

368
    public function listContents($directory = '', /** @scrutinizer ignore-unused */ $recursive = false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
369
    {
370
        try {
371
            list($file_list, $marker, $error) = $this->getBucketManager()->listFiles($this->bucket, static::normalizerPath($directory));
372
            if ($error) {
373
                throw new TinymengException("列出目录文件失败: " . $error->message());
374
            }
375
            $data = [];
376
            if (is_array($file_list)) {
377
                foreach ($file_list as &$file) {
378
                    $data[] = [
379
                        'path'   => $file['key'],
380
                        'marker' => $marker, //用于下次请求的标识符
381
                        'file_type' => isset($file['type']) ? $file['type'] : 'file',
382
                        'file_size' => isset($file['fsize']) ? $file['fsize'] : 0,
383
                        'last_modified' => isset($file['putTime']) ? $file['putTime'] : '',
384
                    ];
385
                }
386
            }
387
            return $data;
388
        } catch (Exception $e) {
389
            if ($e instanceof TinymengException) {
390
                throw $e;
391
            }
392
            throw new TinymengException($e->getMessage());
393
        }
394
    }
395
    /**
396
     * 获取资源的元信息,但不返回文件内容
397
     *
398
     * @param string $path
399
     * @return array|bool
400
     * @throws TinymengException
401
     * @author tinymeng <[email protected]>
402
     */
403
    public function getMetadata($path)
404
    {
405
        try {
406
            list($info, $error) = $this->getBucketManager()->stat($this->bucket, static::normalizerPath($path));
407
            if ($error) {
408
                return false;
409
            }
410
            return $info;
411
        } catch (Exception $e) {
412
            throw new TinymengException($e->getMessage());
413
        }
414
    }
415
    /**
416
     * 获得文件大小
417
     *
418
     * @param string $path
419
     * @return array
420
     * @throws TinymengException
421
     * @author tinymeng <[email protected]>
422
     */
423
    public function getSize($path)
424
    {
425
        try {
426
            $file_info = $this->getMetadata($path);
427
            if ($file_info === false) {
428
                return ['size' => 0];
429
            }
430
            $fsize = isset($file_info['fsize']) ? $file_info['fsize'] : 0;
431
            return $fsize > 0 ? [ 'size' => $fsize ] : ['size' => 0];
432
        } catch (Exception $e) {
433
            if ($e instanceof TinymengException) {
434
                throw $e;
435
            }
436
            throw new TinymengException($e->getMessage());
437
        }
438
    }
439
    /**
440
     * 获得文件Mime类型
441
     *
442
     * @param string $path
443
     * @return array|false
444
     * @throws TinymengException
445
     * @author tinymeng <[email protected]>
446
     */
447
    public function getMimetype($path)
448
    {
449
        try {
450
            $file_info = $this->getMetadata($path);
451
            if ($file_info === false) {
452
                return false;
453
            }
454
            $mimeType = isset($file_info['mimeType']) ? $file_info['mimeType'] : '';
455
            return !empty($mimeType) ? ['mimetype' => $mimeType ] : false;
456
        } catch (Exception $e) {
457
            if ($e instanceof TinymengException) {
458
                throw $e;
459
            }
460
            throw new TinymengException($e->getMessage());
461
        }
462
    }
463
    /**
464
     * 获得文件最后修改时间
465
     *
466
     * @param string $path
467
     * @return array 时间戳
468
     * @throws TinymengException
469
     * @author tinymeng <[email protected]>
470
     */
471
    public function getTimestamp($path)
472
    {
473
        try {
474
            $file_info = $this->getMetadata($path);
475
            if ($file_info === false) {
476
                return ['timestamp' => 0];
477
            }
478
            $timestamp = isset($file_info['putTime']) ? $file_info['putTime'] : 0;
479
            // 七牛的 putTime 是微秒时间戳,需要转换为秒
480
            if ($timestamp > 0) {
481
                $timestamp = intval($timestamp / 10000000);
482
            }
483
            return $timestamp > 0 ? ['timestamp' => $timestamp] : ['timestamp' => 0];
484
        } catch (Exception $e) {
485
            if ($e instanceof TinymengException) {
486
                throw $e;
487
            }
488
            throw new TinymengException($e->getMessage());
489
        }
490
    }
491
    /**
492
     * 获得文件模式 (未实现)
493
     *
494
     * @param string $path
495
     * @author tinymeng <[email protected]>
496
     */
497
    public function getVisibility($path)
0 ignored issues
show
Unused Code introduced by
The parameter $path is not used and could be removed. ( Ignorable by Annotation )

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

497
    public function getVisibility(/** @scrutinizer ignore-unused */ $path)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
498
    {
499
        return self::VISIBILITY_PUBLIC;
500
    }
501
    /**
502
     * 重命名文件
503
     *
504
     * @param string $path
505
     * @param string $newpath
506
     * @return bool
507
     * @throws TinymengException
508
     * @author tinymeng <[email protected]>
509
     */
510
    public function rename($path, $newpath)
511
    {
512
        try {
513
            $error = $this->getBucketManager()->rename($this->bucket, static::normalizerPath($path), static::normalizerPath($newpath));
514
            if ($error !== null) {
515
                throw new TinymengException("文件重命名失败: " . $error->message());
516
            }
517
            return true;
518
        } catch (Exception $e) {
519
            if ($e instanceof TinymengException) {
520
                throw $e;
521
            }
522
            throw new TinymengException($e->getMessage());
523
        }
524
    }
525
    /**
526
     * 复制文件
527
     *
528
     * @param string $path
529
     * @param string $newpath
530
     * @return bool
531
     * @throws TinymengException
532
     * @author tinymeng <[email protected]>
533
     */
534
    public function copy($path, $newpath)
535
    {
536
        try {
537
            $error = $this->getBucketManager()->copy($this->bucket, static::normalizerPath($path), $this->bucket, static::normalizerPath($newpath));
538
            if ($error !== null) {
539
                throw new TinymengException("文件复制失败: " . $error->message());
540
            }
541
            return true;
542
        } catch (Exception $e) {
543
            if ($e instanceof TinymengException) {
544
                throw $e;
545
            }
546
            throw new TinymengException($e->getMessage());
547
        }
548
    }
549
    /**
550
     * 删除文件或者文件夹
551
     *
552
     * @param string $path
553
     * @return bool
554
     * @throws TinymengException
555
     * @author tinymeng <[email protected]>
556
     */
557
    public function delete($path)
558
    {
559
        try {
560
            $error = $this->getBucketManager()->delete($this->bucket, static::normalizerPath($path));
561
            if ($error !== null) {
562
                throw new TinymengException("文件删除失败: " . $error->message());
563
            }
564
            return true;
565
        } catch (Exception $e) {
566
            if ($e instanceof TinymengException) {
567
                throw $e;
568
            }
569
            throw new TinymengException($e->getMessage());
570
        }
571
    }
572
    /**
573
     * 删除文件夹
574
     *
575
     * @param string $path
576
     * @return bool
577
     * @throws TinymengException
578
     * @author tinymeng <[email protected]>
579
     */
580
    public function deleteDir($path)
581
    {
582
        try {
583
            list($file_list, , $error) = $this->getBucketManager()->listFiles($this->bucket, static::normalizerPath($path));
584
            if ($error) {
585
                throw new TinymengException("列出目录文件失败: " . $error->message());
586
            }
587
            if (is_array($file_list)) {
588
                foreach ($file_list as $file) {
589
                    $this->delete($file['key']);
590
                }
591
            }
592
            return true;
593
        } catch (Exception $e) {
594
            if ($e instanceof TinymengException) {
595
                throw $e;
596
            }
597
            throw new TinymengException($e->getMessage());
598
        }
599
    }
600
601
    /**
602
     * 创建文件夹(因为七牛没有文件夹的概念,所以此方法没有实现)
603
     *
604
     * @param string $dirname
605
     * @author tinymeng <[email protected]>
606
     */
607
    public function createDir($dirname)
608
    {
609
        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 tinymeng\uploads\Connect...yInterface::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...
610
    }
611
    /**
612
     * 设置文件模式 (未实现)
613
     *
614
     * @param string $path
615
     * @param string $visibility
616
     * @return bool
617
     * @author tinymeng <[email protected]>
618
     */
619
    public function setVisibility($path, $visibility)
620
    {
621
        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 tinymeng\uploads\Connect...erface::setVisibility() 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...
622
    }
623
    /**
624
     * 获取当前文件的URL访问路径
625
     *
626
     * @param string $file 文件名
627
     * @param int $expire_at 有效期,单位:秒(0 表示永久有效,使用路径前缀)
628
     * @return string
629
     */
630
    public function getUrl($file, $expire_at = 3600)
631
    {
632
        $file = static::normalizerPath($file);
633
        
634
        // 构建完整的基础 URL
635
        $baseUrl = rtrim($this->config['transport'] . '://' . $this->config['domain'], '/');
636
        $filePath = '/' . ltrim($file, '/');
637
        $fullUrl = $baseUrl . $filePath;
638
        
639
        // 如果不需要签名 URL,直接返回完整 URL
640
        if ($expire_at == 0) {
641
            return $fullUrl;
642
        }
643
        
644
        // 生成签名 URL
645
        try {
646
            $signedUrl = $this->auth->privateDownloadUrl($fullUrl, $expire_at);
647
            return $signedUrl;
648
        } catch (Exception $e) {
649
            // 如果生成签名 URL 失败,返回普通 URL
650
            return $fullUrl;
651
        }
652
    }
653
}