Passed
Push — master ( 93ae1f...c8123d )
by TQ
04:05
created

Uploader::needUseBlock()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5.0488

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 3
nop 1
dl 0
loc 10
ccs 7
cts 8
cp 0.875
crap 5.0488
rs 9.6111
c 0
b 0
f 0
1
<?php
2
namespace Upyun;
3
4
use Upyun\Api\Rest;
5
use Upyun\Api\Form;
6
use GuzzleHttp\Psr7;
7
use GuzzleHttp\Pool;
8
use GuzzleHttp\Client;
9
use GuzzleHttp\Psr7\Utils;
10
use GuzzleHttp\Psr7\MimeType;
11
12
class Uploader
13
{
14
    /**
15
     * @var Config
16
     */
17
    protected $config;
18
19
    protected $useBlock = false;
20
21
22 15
    public function __construct(Config $config)
23
    {
24 15
        $this->config = $config;
25
    }
26
27 15
    public function upload($path, $file, $params, $withAsyncProcess)
28
    {
29 15
        $stream = Utils::streamFor($file);
30 15
        $size = $stream->getSize();
31 15
        $useBlock = $this->needUseBlock($size);
32
33 15
        if ($withAsyncProcess) {
34 1
            $req = new Form($this->config);
35 1
            return $req->upload($path, $stream, $params);
36
        }
37
38 14
        if (! $useBlock) {
39 13
            $req = new Rest($this->config);
40 13
            return $req->request('PUT', $path)
41 13
                       ->withHeaders($params)
42 13
                       ->withFile($stream)
43 13
                       ->send();
44 1
        } elseif ($this->config->uploadType === 'BLOCK_PARALLEL') {
45 1
            return $this->concurrentPointUpload($path, $stream, $params);
46
        } else {
47
            return $this->pointUpload($path, $stream, $params);
48
        }
49
    }
50
51
    /**
52
     * 串行式断点续传
53
     * @param $path
54
     * @param $stream
55
     * @param $params
56
     *
57
     * @return mixed|\Psr\Http\Message\ResponseInterface
58
     * @throws \Exception
59
     */
60
    private function pointUpload($path, $stream, $params)
61
    {
62
        $req = new Rest($this->config);
63
        $headers = array();
64
        if (is_array($params)) {
65
            foreach ($params as $key => $val) {
66
                $headers['X-Upyun-Meta-' . $key] = $val;
67
            }
68
        }
69
        $res = $req->request('PUT', $path)
70
            ->withHeaders(array_merge(array(
71
                'X-Upyun-Multi-Stage' => 'initiate',
72
                'X-Upyun-Multi-Type' => MimeType::fromFilename($path),
73
                'X-Upyun-Multi-Length' => $stream->getSize(),
74
            ), $headers))
75
            ->send();
76
        if ($res->getStatusCode() !== 204) {
77
            throw new \Exception('init request failed when poinit upload!');
78
        }
79
80
        $init      = Util::getHeaderParams($res->getHeaders());
81
        $uuid      = $init['x-upyun-multi-uuid'];
82
        $blockSize = 1024 * 1024;
83
        $partId    = 0;
84
        do {
85
            $fileBlock = $stream->read($blockSize);
86
            $res = $req->request('PUT', $path)
87
                ->withHeaders(array(
88
                    'X-Upyun-Multi-Stage' => 'upload',
89
                    'X-Upyun-Multi-Uuid' => $uuid,
90
                    'X-Upyun-Part-Id' => $partId
91
                ))
92
                ->withFile(Utils::streamFor($fileBlock))
93
                ->send();
94
95
            if ($res->getStatusCode() !== 204) {
96
                throw new \Exception('upload request failed when poinit upload!');
97
            }
98
            $data   = Util::getHeaderParams($res->getHeaders());
99
            $partId = $data['x-upyun-next-part-id'];
100
        } while ($partId != -1);
101
102
        $res = $req->request('PUT', $path)
103
            ->withHeaders(array(
104
                'X-Upyun-Multi-Uuid' => $uuid,
105
                'X-Upyun-Multi-Stage' => 'complete'
106
            ))
107
            ->send();
108
109
        if ($res->getStatusCode() != 204 && $res->getStatusCode() != 201) {
110
            throw new \Exception('end request failed when poinit upload!');
111
        }
112
        return $res;
113
    }
114
115 15
    private function needUseBlock($fileSize)
116
    {
117 15
        if ($this->config->uploadType === 'BLOCK' ||
118 15
            $this->config->uploadType === 'BLOCK_PARALLEL') {
119 1
            return true;
120 14
        } elseif ($this->config->uploadType === 'AUTO' &&
121 14
                  $fileSize >= $this->config->sizeBoundary) {
122
            return true;
123
        } else {
124 14
            return false;
125
        }
126
    }
127
128
    /**
129
     * 并行式断点续传
130
     * @param $path
131
     * @param $stream
132
     * @param $params
133
     *
134
     * @return mixed|\Psr\Http\Message\ResponseInterface
135
     * @throws \Exception
136
     */
137 1
    private function concurrentPointUpload($path, $stream, $params)
138
    {
139 1
        $req = new Rest($this->config);
140
141 1
        $headers = array();
142 1
        if (is_array($params)) {
143 1
            foreach ($params as $key => $val) {
144
                $headers['X-Upyun-Meta-' . $key] = $val;
145
            }
146
        }
147 1
        $res = $req->request('PUT', $path)
148 1
            ->withHeaders(array_merge(array(
149 1
                'X-Upyun-Multi-Disorder' => 'true',
150 1
                'X-Upyun-Multi-Stage' => 'initiate',
151 1
                'X-Upyun-Multi-Type' => MimeType::fromFilename($path),
152 1
                'X-Upyun-Multi-Length' => $stream->getSize(),
153 1
            ), $headers))
154 1
            ->send();
155 1
        if ($res->getStatusCode() !== 204) {
156
            throw new \Exception('init request failed when poinit upload!');
157
        }
158
159 1
        $init = Util::getHeaderParams($res->getHeaders());
160 1
        $uuid = $init['x-upyun-multi-uuid'];
161 1
        $requests = function ($req, $path, $stream, $uuid) {
162 1
            $blockSize = 1024 * 1024;
163 1
            $total = ceil($stream->getSize() / $blockSize);
164 1
            for ($i = 0; $i < $total; $i++) {
165 1
                $fileBlock = $stream->read($blockSize);
166 1
                yield $req->request('PUT', $path)
167 1
                    ->withHeaders(array(
168 1
                        'X-Upyun-Multi-Stage' => 'upload',
169 1
                        'X-Upyun-Multi-Uuid' => $uuid,
170 1
                        'X-Upyun-Part-Id' => $i
171 1
                    ))
172 1
                    ->withFile(Utils::streamFor($fileBlock))
173 1
                    ->toRequest();
174
            }
175 1
        };
176 1
        $client = new Client([
177 1
            'timeout' => $this->config->timeout,
178 1
        ]);
179 1
        $pool = new Pool($client, $requests($req, $path, $stream, $uuid), [
180 1
            'concurrency' => $this->config->concurrency,
181 1
            'fulfilled' => function ($res) {
182 1
                if ($res->getStatusCode() !== 204) {
183
                    throw new \Exception('upload request failed when poinit upload!');
184
                }
185 1
            },
186 1
            'rejected' => function () {
187
                throw new \Exception('upload request failed when poinit upload!');
188 1
            },
189 1
        ]);
190 1
        $promise = $pool->promise();
191 1
        $promise->wait();
192
193 1
        $res = $req->request('PUT', $path)
194 1
            ->withHeaders(array(
195 1
                'X-Upyun-Multi-Uuid' => $uuid,
196 1
                'X-Upyun-Multi-Stage' => 'complete'
197 1
            ))
198 1
            ->send();
199 1
        if ($res->getStatusCode() != 204 && $res->getStatusCode() != 201) {
200
            throw new \Exception('end request failed when poinit upload!');
201
        }
202 1
        return $res;
203
    }
204
}
205