PluginProbe ʕ •ᴥ•ʔ
ShareThis Dashboard for Google Analytics / 3.1.2
ShareThis Dashboard for Google Analytics v3.1.2
3.3.2 trunk 1.0.7 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2.5 2.3.5 2.3.6 2.3.7 2.3.8 2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 3.0.0 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.3.0 3.3.1
googleanalytics / lib / analytics-admin / vendor / google / apiclient / src / Http / MediaFileUpload.php
googleanalytics / lib / analytics-admin / vendor / google / apiclient / src / Http Last commit date
Batch.php 3 years ago MediaFileUpload.php 3 years ago REST.php 3 years ago
MediaFileUpload.php
354 lines
1 <?php
2 /**
3 * Copyright 2012 Google Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 namespace Google\Http;
19
20 use Google\Client;
21 use Google\Exception as GoogleException;
22 use GuzzleHttp\Psr7;
23 use GuzzleHttp\Psr7\Request;
24 use GuzzleHttp\Psr7\Uri;
25 use Psr\Http\Message\RequestInterface;
26
27 /**
28 * Manage large file uploads, which may be media but can be any type
29 * of sizable data.
30 */
31 class MediaFileUpload
32 {
33 const UPLOAD_MEDIA_TYPE = 'media';
34 const UPLOAD_MULTIPART_TYPE = 'multipart';
35 const UPLOAD_RESUMABLE_TYPE = 'resumable';
36
37 /** @var string $mimeType */
38 private $mimeType;
39
40 /** @var string $data */
41 private $data;
42
43 /** @var bool $resumable */
44 private $resumable;
45
46 /** @var int $chunkSize */
47 private $chunkSize;
48
49 /** @var int $size */
50 private $size;
51
52 /** @var string $resumeUri */
53 private $resumeUri;
54
55 /** @var int $progress */
56 private $progress;
57
58 /** @var Client */
59 private $client;
60
61 /** @var RequestInterface */
62 private $request;
63
64 /** @var string */
65 private $boundary; // @phpstan-ignore-line
66
67 /**
68 * Result code from last HTTP call
69 * @var int
70 */
71 private $httpResultCode;
72
73 /**
74 * @param Client $client
75 * @param RequestInterface $request
76 * @param string $mimeType
77 * @param string $data The bytes you want to upload.
78 * @param bool $resumable
79 * @param int $chunkSize File will be uploaded in chunks of this many bytes.
80 * only used if resumable=True
81 */
82 public function __construct(
83 Client $client,
84 RequestInterface $request,
85 $mimeType,
86 $data,
87 $resumable = false,
88 $chunkSize = 0
89 ) {
90 $this->client = $client;
91 $this->request = $request;
92 $this->mimeType = $mimeType;
93 $this->data = $data;
94 $this->resumable = $resumable;
95 $this->chunkSize = $chunkSize;
96 $this->progress = 0;
97
98 $this->process();
99 }
100
101 /**
102 * Set the size of the file that is being uploaded.
103 * @param int $size - int file size in bytes
104 */
105 public function setFileSize($size)
106 {
107 $this->size = $size;
108 }
109
110 /**
111 * Return the progress on the upload
112 * @return int progress in bytes uploaded.
113 */
114 public function getProgress()
115 {
116 return $this->progress;
117 }
118
119 /**
120 * Send the next part of the file to upload.
121 * @param string|bool $chunk Optional. The next set of bytes to send. If false will
122 * use $data passed at construct time.
123 */
124 public function nextChunk($chunk = false)
125 {
126 $resumeUri = $this->getResumeUri();
127
128 if (false == $chunk) {
129 $chunk = substr($this->data, $this->progress, $this->chunkSize);
130 }
131
132 $lastBytePos = $this->progress + strlen($chunk) - 1;
133 $headers = [
134 'content-range' => "bytes $this->progress-$lastBytePos/$this->size",
135 'content-length' => (string) strlen($chunk),
136 'expect' => '',
137 ];
138
139 $request = new Request(
140 'PUT',
141 $resumeUri,
142 $headers,
143 Psr7\Utils::streamFor($chunk)
144 );
145
146 return $this->makePutRequest($request);
147 }
148
149 /**
150 * Return the HTTP result code from the last call made.
151 * @return int code
152 */
153 public function getHttpResultCode()
154 {
155 return $this->httpResultCode;
156 }
157
158 /**
159 * Sends a PUT-Request to google drive and parses the response,
160 * setting the appropiate variables from the response()
161 *
162 * @param RequestInterface $request the Request which will be send
163 *
164 * @return false|mixed false when the upload is unfinished or the decoded http response
165 *
166 */
167 private function makePutRequest(RequestInterface $request)
168 {
169 $response = $this->client->execute($request);
170 $this->httpResultCode = $response->getStatusCode();
171
172 if (308 == $this->httpResultCode) {
173 // Track the amount uploaded.
174 $range = $response->getHeaderLine('range');
175 if ($range) {
176 $range_array = explode('-', $range);
177 $this->progress = ((int) $range_array[1]) + 1;
178 }
179
180 // Allow for changing upload URLs.
181 $location = $response->getHeaderLine('location');
182 if ($location) {
183 $this->resumeUri = $location;
184 }
185
186 // No problems, but upload not complete.
187 return false;
188 }
189
190 return REST::decodeHttpResponse($response, $this->request);
191 }
192
193 /**
194 * Resume a previously unfinished upload
195 * @param string $resumeUri the resume-URI of the unfinished, resumable upload.
196 */
197 public function resume($resumeUri)
198 {
199 $this->resumeUri = $resumeUri;
200 $headers = [
201 'content-range' => "bytes */$this->size",
202 'content-length' => '0',
203 ];
204 $httpRequest = new Request(
205 'PUT',
206 $this->resumeUri,
207 $headers
208 );
209 return $this->makePutRequest($httpRequest);
210 }
211
212 /**
213 * @return RequestInterface
214 * @visible for testing
215 */
216 private function process()
217 {
218 $this->transformToUploadUrl();
219 $request = $this->request;
220
221 $postBody = '';
222 $contentType = false;
223
224 $meta = json_decode((string) $request->getBody(), true);
225
226 $uploadType = $this->getUploadType($meta);
227 $request = $request->withUri(
228 Uri::withQueryValue($request->getUri(), 'uploadType', $uploadType)
229 );
230
231 $mimeType = $this->mimeType ?: $request->getHeaderLine('content-type');
232
233 if (self::UPLOAD_RESUMABLE_TYPE == $uploadType) {
234 $contentType = $mimeType;
235 $postBody = is_string($meta) ? $meta : json_encode($meta);
236 } elseif (self::UPLOAD_MEDIA_TYPE == $uploadType) {
237 $contentType = $mimeType;
238 $postBody = $this->data;
239 } elseif (self::UPLOAD_MULTIPART_TYPE == $uploadType) {
240 // This is a multipart/related upload.
241 $boundary = $this->boundary ?: mt_rand();
242 $boundary = str_replace('"', '', $boundary);
243 $contentType = 'multipart/related; boundary=' . $boundary;
244 $related = "--$boundary\r\n";
245 $related .= "Content-Type: application/json; charset=UTF-8\r\n";
246 $related .= "\r\n" . json_encode($meta) . "\r\n";
247 $related .= "--$boundary\r\n";
248 $related .= "Content-Type: $mimeType\r\n";
249 $related .= "Content-Transfer-Encoding: base64\r\n";
250 $related .= "\r\n" . base64_encode($this->data) . "\r\n";
251 $related .= "--$boundary--";
252 $postBody = $related;
253 }
254
255 $request = $request->withBody(Psr7\Utils::streamFor($postBody));
256
257 if ($contentType) {
258 $request = $request->withHeader('content-type', $contentType);
259 }
260
261 return $this->request = $request;
262 }
263
264 /**
265 * Valid upload types:
266 * - resumable (UPLOAD_RESUMABLE_TYPE)
267 * - media (UPLOAD_MEDIA_TYPE)
268 * - multipart (UPLOAD_MULTIPART_TYPE)
269 * @param string|false $meta
270 * @return string
271 * @visible for testing
272 */
273 public function getUploadType($meta)
274 {
275 if ($this->resumable) {
276 return self::UPLOAD_RESUMABLE_TYPE;
277 }
278
279 if (false == $meta && $this->data) {
280 return self::UPLOAD_MEDIA_TYPE;
281 }
282
283 return self::UPLOAD_MULTIPART_TYPE;
284 }
285
286 public function getResumeUri()
287 {
288 if (null === $this->resumeUri) {
289 $this->resumeUri = $this->fetchResumeUri();
290 }
291
292 return $this->resumeUri;
293 }
294
295 private function fetchResumeUri()
296 {
297 $body = $this->request->getBody();
298 $headers = [
299 'content-type' => 'application/json; charset=UTF-8',
300 'content-length' => $body->getSize(),
301 'x-upload-content-type' => $this->mimeType,
302 'x-upload-content-length' => $this->size,
303 'expect' => '',
304 ];
305 foreach ($headers as $key => $value) {
306 $this->request = $this->request->withHeader($key, $value);
307 }
308
309 $response = $this->client->execute($this->request, false);
310 $location = $response->getHeaderLine('location');
311 $code = $response->getStatusCode();
312
313 if (200 == $code && true == $location) {
314 return $location;
315 }
316
317 $message = $code;
318 $body = json_decode((string) $this->request->getBody(), true);
319 if (isset($body['error']['errors'])) {
320 $message .= ': ';
321 foreach ($body['error']['errors'] as $error) {
322 $message .= "{$error['domain']}, {$error['message']};";
323 }
324 $message = rtrim($message, ';');
325 }
326
327 $error = "Failed to start the resumable upload (HTTP {$message})";
328 $this->client->getLogger()->error($error);
329
330 throw new GoogleException($error);
331 }
332
333 private function transformToUploadUrl()
334 {
335 $parts = parse_url((string) $this->request->getUri());
336 if (!isset($parts['path'])) {
337 $parts['path'] = '';
338 }
339 $parts['path'] = '/upload' . $parts['path'];
340 $uri = Uri::fromParts($parts);
341 $this->request = $this->request->withUri($uri);
342 }
343
344 public function setChunkSize($chunkSize)
345 {
346 $this->chunkSize = $chunkSize;
347 }
348
349 public function getRequest()
350 {
351 return $this->request;
352 }
353 }
354