Completed
Pull Request — master (#228)
by r
04:17
created

ResumeUploader::fileUrl()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 4
nop 0
dl 0
loc 15
ccs 0
cts 13
cp 0
crap 20
rs 9.2
c 0
b 0
f 0
1
<?php
2
namespace Qiniu\Storage;
3
4
use Qiniu\Config;
5
use Qiniu\Http\Client;
6
use Qiniu\Http\Error;
7
8
/**
9
 * 断点续上传类, 该类主要实现了断点续上传中的分块上传,
10
 * 以及相应地创建块和创建文件过程.
11
 *
12
 * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkblk.html
13
 * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkfile.html
14
 */
15
final class ResumeUploader
16
{
17
    private $upToken;
18
    private $key;
19
    private $inputStream;
20
    private $size;
21
    private $params;
22
    private $mime;
23
    private $contexts;
24
    private $host;
25
    private $currentUrl;
26
    private $config;
27
28
    /**
29
     * 上传二进制流到七牛
30
     *
31
     * @param $upToken    上传凭证
32
     * @param $key        上传文件名
33
     * @param $inputStream 上传二进制流
34
     * @param $size       上传流的大小
35
     * @param $params     自定义变量
36
     * @param $mime       上传数据的mimeType
37
     *
38
     * @link http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
39
     */
40 6
    public function __construct(
41
        $upToken,
42
        $key,
43
        $inputStream,
44
        $size,
45
        $params,
46
        $mime,
47
        $config
48
    ) {
49 6
        $this->upToken = $upToken;
50 6
        $this->key = $key;
51 6
        $this->inputStream = $inputStream;
52 6
        $this->size = $size;
53 6
        $this->params = $params;
54 6
        $this->mime = $mime;
55 6
        $this->contexts = array();
56 6
        $this->config = $config;
57
58 6
        list($upHost, $err) = $config->zone->getUpHostByToken($upToken);
59 6
        if ($err != null) {
60 6
            throw new \Exception($err->message(), 1);
61
        }
62
        $this->host = $upHost;
63
    }
64
65
    /**
66
     * 上传操作
67
     */
68
    public function upload()
69
    {
70
        $uploaded = 0;
71
        while ($uploaded < $this->size) {
72
            $blockSize = $this->blockSize($uploaded);
73
            $data = fread($this->inputStream, $blockSize);
74
            if ($data === false) {
75
                throw new \Exception("file read failed", 1);
76
            }
77
            $crc = \Qiniu\crc32_data($data);
78
            $response = $this->makeBlock($data, $blockSize);
79
            $ret = null;
80
            if ($response->ok() && $response->json() != null) {
81
                $ret = $response->json();
82
            }
83
            if ($response->statusCode < 0) {
84
                list($bakHost, $err) = $this->config->zone->getBackupUpHostByToken($this->upToken);
85
                if ($err != null) {
86
                    return array(null, $err);
87
                }
88
                $this->host = $bakHost;
89
            }
90
            if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
91
                $response = $this->makeBlock($data, $blockSize);
92
                $ret = $response->json();
93
            }
94
95
            if (! $response->ok() || !isset($ret['crc32'])|| $crc != $ret['crc32']) {
96
                return array(null, new Error($this->currentUrl, $response));
97
            }
98
            array_push($this->contexts, $ret['ctx']);
99
            $uploaded += $blockSize;
100
        }
101
        return $this->makeFile();
102
    }
103
104
    /**
105
     * 创建块
106
     */
107
    private function makeBlock($block, $blockSize)
108
    {
109
        $url = $this->host . '/mkblk/' . $blockSize;
110
        return $this->post($url, $block);
111
    }
112
113
    private function fileUrl()
114
    {
115
        $url = $this->host . '/mkfile/' . $this->size;
116
        $url .= '/mimeType/' . \Qiniu\base64_urlSafeEncode($this->mime);
117
        if ($this->key != null) {
118
            $url .= '/key/' . \Qiniu\base64_urlSafeEncode($this->key);
119
        }
120
        if (!empty($this->params)) {
121
            foreach ($this->params as $key => $value) {
122
                $val =  \Qiniu\base64_urlSafeEncode($value);
123
                $url .= "/$key/$val";
124
            }
125
        }
126
        return $url;
127
    }
128
129
    /**
130
     * 创建文件
131
     */
132
    private function makeFile()
133
    {
134
        $url = $this->fileUrl();
135
        $body = implode(',', $this->contexts);
136
        $response = $this->post($url, $body);
137
        if ($response->needRetry()) {
138
            $response = $this->post($url, $body);
139
        }
140
        if (! $response->ok()) {
141
            return array(null, new Error($this->currentUrl, $response));
142
        }
143
        return array($response->json(), null);
144
    }
145
146
    private function post($url, $data)
147
    {
148
        $this->currentUrl = $url;
149
        $headers = array('Authorization' => 'UpToken ' . $this->upToken);
150
        return Client::post($url, $data, $headers);
151
    }
152
153
    private function blockSize($uploaded)
154
    {
155
        if ($this->size < $uploaded + Config::BLOCK_SIZE) {
156
            return $this->size - $uploaded;
157
        }
158
        return  Config::BLOCK_SIZE;
159
    }
160
}
161