1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Srmklive\Dropbox\Client; |
4
|
|
|
|
5
|
|
|
use GuzzleHttp\Client as HttpClient; |
6
|
|
|
use GuzzleHttp\Exception\ClientException as HttpClientException; |
7
|
|
|
use GuzzleHttp\Psr7\StreamWrapper; |
8
|
|
|
use Illuminate\Support\Collection; |
9
|
|
|
use Srmklive\Dropbox\DropboxUploadCounter; |
10
|
|
|
use Srmklive\Dropbox\Exceptions\BadRequest; |
11
|
|
|
|
12
|
|
|
class DropboxClient |
13
|
|
|
{ |
14
|
|
|
const THUMBNAIL_FORMAT_JPEG = 'jpeg'; |
15
|
|
|
const THUMBNAIL_FORMAT_PNG = 'png'; |
16
|
|
|
|
17
|
|
|
const THUMBNAIL_SIZE_XS = 'w32h32'; |
18
|
|
|
const THUMBNAIL_SIZE_S = 'w64h64'; |
19
|
|
|
const THUMBNAIL_SIZE_M = 'w128h128'; |
20
|
|
|
const THUMBNAIL_SIZE_L = 'w640h480'; |
21
|
|
|
const THUMBNAIL_SIZE_XL = 'w1024h768'; |
22
|
|
|
|
23
|
|
|
const MAX_CHUNK_SIZE = 150 * 1024 * 1024; |
24
|
|
|
|
25
|
|
|
/** @var \GuzzleHttp\Client */ |
26
|
|
|
protected $client; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* Dropbox OAuth access token. |
30
|
|
|
* |
31
|
|
|
* @var string |
32
|
|
|
*/ |
33
|
|
|
protected $accessToken; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Dropbox API v2 Url. |
37
|
|
|
* |
38
|
|
|
* @var string |
39
|
|
|
*/ |
40
|
|
|
protected $apiUrl; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Dropbox content API v2 url for uploading content. |
44
|
|
|
* |
45
|
|
|
* @var string |
46
|
|
|
*/ |
47
|
|
|
protected $apiContentUrl; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Dropbox API v2 endpoint. |
51
|
|
|
* |
52
|
|
|
* @var string |
53
|
|
|
*/ |
54
|
|
|
protected $apiEndpoint; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var mixed |
58
|
|
|
*/ |
59
|
|
|
protected $content; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Collection containing Dropbox API request data. |
63
|
|
|
* |
64
|
|
|
* @var \Illuminate\Support\Collection |
65
|
|
|
*/ |
66
|
|
|
protected $request; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* DropboxClient constructor. |
70
|
|
|
* |
71
|
12 |
|
* @param string $token |
72
|
|
|
* @param \GuzzleHttp\Client $client |
73
|
12 |
|
*/ |
74
|
|
|
public function __construct($token, HttpClient $client = null) |
75
|
12 |
|
{ |
76
|
|
|
$this->setAccessToken($token); |
77
|
12 |
|
|
78
|
12 |
|
$this->setClient($client); |
79
|
12 |
|
|
80
|
|
|
$this->apiUrl = 'https://api.dropboxapi.com/2/'; |
81
|
|
|
$this->apiContentUrl = 'https://content.dropboxapi.com/2/'; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Set Http Client. |
86
|
12 |
|
* |
87
|
|
|
* @param \GuzzleHttp\Client $client |
88
|
12 |
|
*/ |
89
|
11 |
|
protected function setClient(HttpClient $client = null) |
90
|
11 |
|
{ |
91
|
1 |
|
if ($client instanceof HttpClient) { |
92
|
|
|
$this->client = $client; |
93
|
1 |
|
} else { |
94
|
1 |
|
$this->client = new HttpClient([ |
95
|
1 |
|
'headers' => [ |
96
|
|
|
'Authorization' => "Bearer {$this->accessToken}", |
97
|
12 |
|
], |
98
|
|
|
]); |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Set Dropbox OAuth access token. |
104
|
12 |
|
* |
105
|
|
|
* @param string $token |
106
|
12 |
|
*/ |
107
|
12 |
|
protected function setAccessToken($token) |
108
|
|
|
{ |
109
|
|
|
$this->accessToken = $token; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Copy a file or folder to a different location in the user's Dropbox. |
114
|
|
|
* |
115
|
|
|
* If the source path is a folder all its contents will be copied. |
116
|
|
|
* |
117
|
|
|
* @param string $fromPath |
118
|
|
|
* @param string $toPath |
119
|
|
|
* |
120
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
121
|
1 |
|
* |
122
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy |
123
|
1 |
|
*/ |
124
|
1 |
View Code Duplication |
public function copy($fromPath, $toPath) |
|
|
|
|
125
|
1 |
|
{ |
126
|
1 |
|
$this->setupRequest([ |
127
|
|
|
'from_path' => $this->normalizePath($fromPath), |
128
|
1 |
|
'to_path' => $this->normalizePath($toPath), |
129
|
|
|
]); |
130
|
1 |
|
|
131
|
|
|
$this->apiEndpoint = 'files/copy'; |
132
|
|
|
|
133
|
|
|
return $this->doDropboxApiRequest(); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Create a folder at a given path. |
138
|
|
|
* |
139
|
|
|
* @param string $path |
140
|
|
|
* |
141
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
142
|
1 |
|
* |
143
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder |
144
|
1 |
|
*/ |
145
|
1 |
|
public function createFolder($path) |
146
|
1 |
|
{ |
147
|
|
|
$this->setupRequest([ |
148
|
1 |
|
'path' => $this->normalizePath($path), |
149
|
|
|
]); |
150
|
1 |
|
|
151
|
1 |
|
$this->apiEndpoint = 'files/create_folder'; |
152
|
|
|
|
153
|
1 |
|
$response = $this->doDropboxApiRequest(); |
154
|
|
|
$response['.tag'] = 'folder'; |
155
|
|
|
|
156
|
|
|
return $response; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Delete the file or folder at a given path. |
161
|
|
|
* |
162
|
|
|
* If the path is a folder, all its contents will be deleted too. |
163
|
|
|
* A successful response indicates that the file or folder was deleted. |
164
|
|
|
* |
165
|
|
|
* @param string $path |
166
|
|
|
* |
167
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
168
|
1 |
|
* |
169
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-delete |
170
|
1 |
|
*/ |
171
|
1 |
|
public function delete($path) |
172
|
1 |
|
{ |
173
|
|
|
$this->setupRequest([ |
174
|
1 |
|
'path' => $this->normalizePath($path), |
175
|
|
|
]); |
176
|
1 |
|
|
177
|
|
|
$this->apiEndpoint = 'files/delete'; |
178
|
|
|
|
179
|
|
|
return $this->doDropboxApiRequest(); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Download a file from a user's Dropbox. |
184
|
|
|
* |
185
|
|
|
* @param string $path |
186
|
|
|
* |
187
|
|
|
* @return resource |
188
|
1 |
|
* |
189
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-download |
190
|
1 |
|
*/ |
191
|
1 |
|
public function download($path) |
192
|
1 |
|
{ |
193
|
|
|
$this->setupRequest([ |
194
|
1 |
|
'path' => $this->normalizePath($path), |
195
|
|
|
]); |
196
|
1 |
|
|
197
|
|
|
$this->apiEndpoint = 'files/download'; |
198
|
1 |
|
|
199
|
|
|
$response = $this->doDropboxApiContentRequest(); |
200
|
|
|
|
201
|
|
|
return StreamWrapper::getResource($response->getBody()); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Returns the metadata for a file or folder. |
206
|
|
|
* |
207
|
|
|
* Note: Metadata for the root folder is unsupported. |
208
|
|
|
* |
209
|
|
|
* @param string $path |
210
|
|
|
* |
211
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
212
|
1 |
|
* |
213
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata |
214
|
1 |
|
*/ |
215
|
1 |
|
public function getMetaData($path) |
216
|
1 |
|
{ |
217
|
|
|
$this->setupRequest([ |
218
|
1 |
|
'path' => $this->normalizePath($path), |
219
|
|
|
]); |
220
|
1 |
|
|
221
|
|
|
$this->apiEndpoint = 'files/get_metadata'; |
222
|
|
|
|
223
|
|
|
return $this->doDropboxApiRequest(); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* Get a temporary link to stream content of a file. |
228
|
|
|
* |
229
|
|
|
* This link will expire in four hours and afterwards you will get 410 Gone. |
230
|
|
|
* Content-Type of the link is determined automatically by the file's mime type. |
231
|
|
|
* |
232
|
|
|
* @param string $path |
233
|
|
|
* |
234
|
|
|
* @return string |
235
|
1 |
|
* |
236
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link |
237
|
1 |
|
*/ |
238
|
1 |
View Code Duplication |
public function getTemporaryLink($path) |
|
|
|
|
239
|
1 |
|
{ |
240
|
|
|
$this->setupRequest([ |
241
|
1 |
|
'path' => $this->normalizePath($path), |
242
|
|
|
]); |
243
|
1 |
|
|
244
|
|
|
$this->apiEndpoint = 'files/get_temporary_link'; |
245
|
1 |
|
|
246
|
|
|
$response = $this->doDropboxApiRequest(); |
247
|
|
|
|
248
|
|
|
return $response['link']; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Get a thumbnail for an image. |
253
|
|
|
* |
254
|
|
|
* This method currently supports files with the following file extensions: |
255
|
|
|
* jpg, jpeg, png, tiff, tif, gif and bmp. |
256
|
|
|
* |
257
|
|
|
* Photos that are larger than 20MB in size won't be converted to a thumbnail. |
258
|
|
|
* |
259
|
|
|
* @param string $path |
260
|
|
|
* @param string $format |
261
|
|
|
* @param string $size |
262
|
1 |
|
* |
263
|
|
|
* @return string |
264
|
1 |
|
*/ |
265
|
1 |
|
public function getThumbnail($path, $format = 'jpeg', $size = 'w64h64') |
266
|
1 |
|
{ |
267
|
1 |
|
$this->setupRequest([ |
268
|
1 |
|
'path' => $this->normalizePath($path), |
269
|
|
|
'format' => $format, |
270
|
1 |
|
'size' => $size, |
271
|
|
|
]); |
272
|
1 |
|
|
273
|
|
|
$this->apiEndpoint = 'files/get_thumbnail'; |
274
|
1 |
|
|
275
|
|
|
$response = $this->doDropboxApiContentRequest(); |
276
|
|
|
|
277
|
|
|
return (string) $response->getBody(); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Starts returning the contents of a folder. |
282
|
|
|
* |
283
|
|
|
* If the result's ListFolderResult.has_more field is true, call |
284
|
|
|
* list_folder/continue with the returned ListFolderResult.cursor to retrieve more entries. |
285
|
|
|
* |
286
|
|
|
* Note: auth.RateLimitError may be returned if multiple list_folder or list_folder/continue calls |
287
|
|
|
* with same parameters are made simultaneously by same API app for same user. If your app implements |
288
|
|
|
* retry logic, please hold off the retry until the previous request finishes. |
289
|
|
|
* |
290
|
|
|
* @param string $path |
291
|
|
|
* @param bool $recursive |
292
|
|
|
* |
293
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
294
|
1 |
|
* |
295
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder |
296
|
1 |
|
*/ |
297
|
1 |
View Code Duplication |
public function listFolder($path = '', $recursive = false) |
|
|
|
|
298
|
1 |
|
{ |
299
|
1 |
|
$this->setupRequest([ |
300
|
|
|
'path' => $this->normalizePath($path), |
301
|
1 |
|
'recursive' => $recursive, |
302
|
|
|
]); |
303
|
1 |
|
|
304
|
|
|
$this->apiEndpoint = 'files/list_folder'; |
305
|
|
|
|
306
|
|
|
return $this->doDropboxApiRequest(); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Once a cursor has been retrieved from list_folder, use this to paginate through all files and |
311
|
|
|
* retrieve updates to the folder, following the same rules as documented for list_folder. |
312
|
|
|
* |
313
|
|
|
* @param string $cursor |
314
|
|
|
* |
315
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
316
|
1 |
|
* |
317
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder-continue |
318
|
1 |
|
*/ |
319
|
1 |
|
public function listFolderContinue($cursor = '') |
320
|
1 |
|
{ |
321
|
|
|
$this->setupRequest([ |
322
|
1 |
|
'cursor' => $cursor, |
323
|
|
|
]); |
324
|
1 |
|
|
325
|
|
|
$this->apiEndpoint = 'files/list_folder/continue'; |
326
|
|
|
|
327
|
|
|
return $this->doDropboxApiRequest(); |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* Move a file or folder to a different location in the user's Dropbox. |
332
|
|
|
* |
333
|
|
|
* If the source path is a folder all its contents will be moved. |
334
|
|
|
* |
335
|
|
|
* @param string $fromPath |
336
|
|
|
* @param string $toPath |
337
|
|
|
* |
338
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
339
|
1 |
|
* |
340
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-move |
341
|
1 |
|
*/ |
342
|
1 |
View Code Duplication |
public function move($fromPath, $toPath) |
|
|
|
|
343
|
1 |
|
{ |
344
|
1 |
|
$this->setupRequest([ |
345
|
|
|
'from_path' => $this->normalizePath($fromPath), |
346
|
1 |
|
'to_path' => $this->normalizePath($toPath), |
347
|
|
|
]); |
348
|
1 |
|
|
349
|
|
|
$this->apiEndpoint = 'files/move_v2'; |
350
|
|
|
|
351
|
|
|
return $this->doDropboxApiRequest(); |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* The file should be uploaded in chunks if it size exceeds the 150 MB threshold |
356
|
|
|
* or if the resource size could not be determined (eg. a popen() stream). |
357
|
|
|
* |
358
|
|
|
* @param string|resource $contents |
359
|
|
|
* |
360
|
|
|
* @return bool |
361
|
|
|
*/ |
362
|
|
|
protected function shouldUploadChunk($contents) |
363
|
|
|
{ |
364
|
1 |
|
$size = is_string($contents) ? strlen($contents) : fstat($contents)['size']; |
365
|
|
|
|
366
|
1 |
|
if ($this->isPipe($contents)) { |
367
|
1 |
|
return true; |
368
|
1 |
|
} |
369
|
1 |
|
|
370
|
|
|
if ($size === null) { |
371
|
1 |
|
return true; |
372
|
|
|
} |
373
|
1 |
|
|
374
|
|
|
return $size > static::MAX_CHUNK_SIZE; |
375
|
1 |
|
} |
376
|
|
|
|
377
|
1 |
|
/** |
378
|
1 |
|
* Check if the contents is a pipe stream (not seekable, no size defined). |
379
|
|
|
* |
380
|
1 |
|
* @param string|resource $contents |
381
|
|
|
* |
382
|
|
|
* @return bool |
383
|
|
|
*/ |
384
|
|
|
protected function isPipe($contents) |
385
|
|
|
{ |
386
|
|
|
return is_resource($contents) ? (fstat($contents)['mode'] & 010000) != 0 : false; |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* Create a new file with the contents provided in the request. |
391
|
|
|
* |
392
|
|
|
* Do not use this to upload a file larger than 150 MB. Instead, create an upload session with upload_session/start. |
393
|
|
|
* |
394
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload |
395
|
|
|
* |
396
|
|
|
* @param string $path |
397
|
|
|
* @param string|resource $contents |
398
|
|
|
* @param string|array $mode |
399
|
|
|
* |
400
|
|
|
* @return array |
401
|
|
|
*/ |
402
|
|
|
public function upload($path, $contents, $mode = 'add') |
403
|
|
|
{ |
404
|
|
|
if ($this->shouldUploadChunk($contents)) { |
405
|
|
|
return $this->uploadChunk($path, $contents, $mode); |
|
|
|
|
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
$this->setupRequest([ |
409
|
|
|
'path' => $this->normalizePath($path), |
410
|
|
|
'mode' => $mode, |
411
|
|
|
]); |
412
|
|
|
|
413
|
|
|
$this->content = $contents; |
414
|
|
|
|
415
|
|
|
$this->apiEndpoint = 'files/upload'; |
416
|
11 |
|
|
417
|
|
|
$response = $this->doDropboxApiContentRequest(); |
418
|
11 |
|
|
419
|
11 |
|
$metadata = json_decode($response->getBody(), true); |
420
|
|
|
$metadata['.tag'] = 'file'; |
421
|
|
|
|
422
|
|
|
return $metadata; |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
/** |
426
|
|
|
* Upload file split in chunks. This allows uploading large files, since |
427
|
|
|
* Dropbox API v2 limits the content size to 150MB. |
428
|
8 |
|
* |
429
|
|
|
* The chunk size will affect directly the memory usage, so be careful. |
430
|
|
|
* Large chunks tends to speed up the upload, while smaller optimizes memory usage. |
431
|
8 |
|
* |
432
|
8 |
|
* @param string $path |
433
|
8 |
|
* @param string|resource $contents |
434
|
8 |
|
* @param string $mode |
435
|
|
|
* @param int $chunkSize |
436
|
|
|
* |
437
|
|
|
* @return array |
438
|
7 |
|
*/ |
439
|
|
|
public function uploadChunk(string $path, $contents, $mode = 'add', $chunkSize = null) |
440
|
|
|
{ |
441
|
|
|
$chunkSize = $chunkSize ?? static::MAX_CHUNK_SIZE; |
442
|
|
|
$stream = $contents; |
443
|
|
|
|
444
|
|
|
// This method relies on resources, so we need to convert strings to resource |
445
|
|
|
if (is_string($contents)) { |
446
|
3 |
|
$stream = fopen('php://memory', 'r+'); |
447
|
|
|
fwrite($stream, $contents); |
448
|
|
|
rewind($stream); |
449
|
3 |
|
} |
450
|
3 |
|
|
451
|
3 |
|
$data = self::readChunk($stream, $chunkSize); |
|
|
|
|
452
|
3 |
|
$cursor = null; |
453
|
|
|
|
454
|
3 |
|
while (! ((strlen($data) < $chunkSize) || feof($stream))) { |
455
|
1 |
|
// Start upload session on first iteration, then just append on subsequent iterations |
456
|
1 |
|
$cursor = isset($cursor) ? $this->appendContentToUploadSession($data, $cursor) : $this->startUploadSession($data); |
457
|
|
|
$data = self::readChunk($stream, $chunkSize); |
|
|
|
|
458
|
3 |
|
} |
459
|
|
|
|
460
|
|
|
// If there's no cursor here, our stream is small enough to a single request |
461
|
|
|
if (! isset($cursor)) { |
462
|
|
|
$cursor = $this->startUploadSession($data); |
463
|
|
|
$data = ''; |
464
|
|
|
} |
465
|
|
|
|
466
|
|
|
return $this->finishUploadSession($data, $cursor, $path, $mode); |
467
|
|
|
} |
468
|
3 |
|
|
469
|
|
|
/** |
470
|
|
|
* Upload sessions allow you to upload a single file in one or more requests, |
471
|
3 |
|
* for example where the size of the file is greater than 150 MB. |
472
|
3 |
|
* This call starts a new upload session with the given data. |
473
|
3 |
|
* |
474
|
3 |
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-start |
475
|
3 |
|
* |
476
|
|
|
* @param string $contents |
477
|
|
|
* @param bool $close |
478
|
|
|
* |
479
|
3 |
|
* @return \Srmklive\Dropbox\DropboxUploadCounter |
480
|
|
|
*/ |
481
|
|
|
public function startUploadSession($contents, bool $close = false) |
482
|
|
|
{ |
483
|
|
|
$this->setupRequest( |
484
|
|
|
compact('close') |
485
|
|
|
); |
486
|
|
|
|
487
|
|
|
$this->apiEndpoint = 'files/upload_session/start'; |
488
|
|
|
|
489
|
10 |
|
$this->content = $contents; |
490
|
|
|
|
491
|
10 |
|
$response = json_decode( |
492
|
|
|
$this->doDropboxApiContentRequest()->getBody(), |
493
|
10 |
|
true |
494
|
|
|
); |
495
|
|
|
|
496
|
|
|
return new DropboxUploadCounter($response['session_id'], strlen($contents)); |
497
|
|
|
} |
498
|
|
|
|
499
|
|
|
|
500
|
|
|
/** |
501
|
|
|
* Append more data to an upload session. |
502
|
|
|
* When the parameter close is set, this call will close the session. |
503
|
|
|
* A single request should not upload more than 150 MB. |
504
|
|
|
* |
505
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-append_v2 |
506
|
|
|
* |
507
|
|
|
* @param string $contents |
508
|
|
|
* @param DropboxUploadCounter $cursor |
509
|
|
|
* @param bool $close |
510
|
|
|
* |
511
|
|
|
* @return \Srmklive\Dropbox\DropboxUploadCounter |
512
|
|
|
*/ |
513
|
|
|
public function appendContentToUploadSession($contents, DropboxUploadCounter $cursor, bool $close = false) |
514
|
|
|
{ |
515
|
|
|
$this->setupRequest(compact('cursor', 'close')); |
516
|
|
|
|
517
|
|
|
$this->apiEndpoint = 'files/upload_session/append_v2'; |
518
|
|
|
|
519
|
|
|
$this->content = $contents; |
520
|
|
|
|
521
|
|
|
$this->doDropboxApiContentRequest()->getBody(); |
522
|
|
|
|
523
|
|
|
$cursor->offset += strlen($contents); |
524
|
|
|
|
525
|
|
|
return $cursor; |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
/** |
529
|
|
|
* Finish an upload session and save the uploaded data to the given file path. |
530
|
|
|
* A single request should not upload more than 150 MB. |
531
|
|
|
* |
532
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload_session-finish |
533
|
|
|
* |
534
|
|
|
* @param string $contents |
535
|
|
|
* @param \Srmklive\Dropbox\DropboxUploadCounter $cursor |
536
|
|
|
* @param string $path |
537
|
|
|
* @param string|array $mode |
538
|
|
|
* @param bool $autorename |
539
|
|
|
* @param bool $mute |
540
|
|
|
* |
541
|
|
|
* @return array |
542
|
|
|
*/ |
543
|
|
|
public function finishUploadSession($contents, DropboxUploadCounter $cursor, string $path, $mode = 'add', $autorename = false, $mute = false) |
544
|
|
|
{ |
545
|
|
|
$arguments = compact('cursor'); |
546
|
|
|
$arguments['commit'] = compact('path', 'mode', 'autorename', 'mute'); |
547
|
|
|
|
548
|
|
|
$this->setupRequest($arguments); |
549
|
|
|
|
550
|
|
|
$this->apiEndpoint = 'files/upload_session/finish'; |
551
|
|
|
|
552
|
|
|
$this->content = $contents; |
553
|
|
|
|
554
|
|
|
$response = $this->doDropboxApiContentRequest(); |
555
|
|
|
|
556
|
|
|
$metadata = json_decode($response->getBody(), true); |
557
|
|
|
|
558
|
|
|
$metadata['.tag'] = 'file'; |
559
|
|
|
|
560
|
|
|
return $metadata; |
561
|
|
|
} |
562
|
|
|
|
563
|
|
|
/** |
564
|
|
|
* Sometimes fread() returns less than the request number of bytes (for example, when reading |
565
|
|
|
* from network streams). This function repeatedly calls fread until the requested number of |
566
|
|
|
* bytes have been read or we've reached EOF. |
567
|
|
|
* |
568
|
|
|
* @param resource $stream |
569
|
|
|
* @param int $chunkSize |
570
|
|
|
* |
571
|
|
|
* @throws \Exception |
572
|
|
|
* @return string |
573
|
|
|
*/ |
574
|
|
|
protected static function readChunk($stream, int $chunkSize) |
575
|
|
|
{ |
576
|
|
|
$chunk = ''; |
577
|
|
|
while (! feof($stream) && $chunkSize > 0) { |
578
|
|
|
$part = fread($stream, $chunkSize); |
579
|
|
|
|
580
|
|
|
if ($part === false) { |
581
|
|
|
throw new \Exception('Error reading from $stream.'); |
582
|
|
|
} |
583
|
|
|
|
584
|
|
|
$chunk .= $part; |
585
|
|
|
$chunkSize -= strlen($part); |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
return $chunk; |
589
|
|
|
} |
590
|
|
|
|
591
|
|
|
/** |
592
|
|
|
* Get Account Info for current authenticated user. |
593
|
|
|
* |
594
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account |
595
|
|
|
* |
596
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
597
|
|
|
*/ |
598
|
|
|
public function getAccountInfo() |
599
|
|
|
{ |
600
|
|
|
$this->apiEndpoint = 'users/get_current_account'; |
601
|
|
|
|
602
|
|
|
return $this->doDropboxApiRequest(); |
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
/** |
606
|
|
|
* Revoke current access token. |
607
|
|
|
* |
608
|
|
|
* @link https://www.dropbox.com/developers/documentation/http/documentation#auth-token-revoke |
609
|
|
|
* |
610
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
611
|
|
|
*/ |
612
|
|
|
public function revokeToken() |
613
|
|
|
{ |
614
|
|
|
$this->apiEndpoint = 'auth/token/revoke'; |
615
|
|
|
|
616
|
|
|
return $this->doDropboxApiRequest(); |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
/** |
620
|
|
|
* Set Dropbox API request data. |
621
|
|
|
* |
622
|
|
|
* @param array $request |
623
|
|
|
*/ |
624
|
|
|
protected function setupRequest($request) |
625
|
|
|
{ |
626
|
|
|
$this->request = new Collection($request); |
627
|
|
|
} |
628
|
|
|
|
629
|
|
|
/** |
630
|
|
|
* Perform Dropbox API request. |
631
|
|
|
* |
632
|
|
|
* @throws \Exception |
633
|
|
|
* |
634
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
635
|
|
|
*/ |
636
|
|
|
protected function doDropboxApiRequest() |
637
|
|
|
{ |
638
|
|
|
$request = empty($this->request) ? [] : ['json' => $this->request->toArray()]; |
639
|
|
|
|
640
|
|
|
try { |
641
|
|
|
$response = $this->client->post("{$this->apiUrl}{$this->apiEndpoint}", $request); |
642
|
|
|
} catch (HttpClientException $exception) { |
643
|
|
|
throw $this->determineException($exception); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
return json_decode($response->getBody(), true); |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
/** |
650
|
|
|
* Setup headers for Dropbox API request. |
651
|
|
|
* |
652
|
|
|
* @return array |
653
|
|
|
*/ |
654
|
|
|
protected function setupDropboxHeaders() |
655
|
|
|
{ |
656
|
|
|
$headers = [ |
657
|
|
|
'Dropbox-API-Arg' => json_encode( |
658
|
|
|
$this->request->toArray() |
659
|
|
|
), |
660
|
|
|
]; |
661
|
|
|
|
662
|
|
|
if (!empty($this->content)) { |
663
|
|
|
$headers['Content-Type'] = 'application/octet-stream'; |
664
|
|
|
} |
665
|
|
|
|
666
|
|
|
return $headers; |
667
|
|
|
} |
668
|
|
|
|
669
|
|
|
/** |
670
|
|
|
* Perform Dropbox API request. |
671
|
|
|
* |
672
|
|
|
* @throws \Exception |
673
|
|
|
* |
674
|
|
|
* @return \Psr\Http\Message\ResponseInterface |
675
|
|
|
*/ |
676
|
|
|
protected function doDropboxApiContentRequest() |
677
|
|
|
{ |
678
|
|
|
try { |
679
|
|
|
$response = $this->client->post("{$this->apiContentUrl}{$this->apiEndpoint}", [ |
680
|
|
|
'headers' => $this->setupDropboxHeaders(), |
681
|
|
|
'body' => !empty($this->content) ? $this->content : '', |
682
|
|
|
]); |
683
|
|
|
} catch (HttpClientException $exception) { |
684
|
|
|
throw $this->determineException($exception); |
685
|
|
|
} |
686
|
|
|
|
687
|
|
|
return $response; |
688
|
|
|
} |
689
|
|
|
|
690
|
|
|
/** |
691
|
|
|
* Normalize path. |
692
|
|
|
* |
693
|
|
|
* @param string $path |
694
|
|
|
* |
695
|
|
|
* @return string |
696
|
|
|
*/ |
697
|
|
|
protected function normalizePath($path) |
698
|
|
|
{ |
699
|
|
|
$path = (trim($path, '/') === '') ? '' : '/'.$path; |
700
|
|
|
|
701
|
|
|
return str_replace('//', '/', $path); |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
/** |
705
|
|
|
* Catch Dropbox API request exception. |
706
|
|
|
* |
707
|
|
|
* @param HttpClientException $exception |
708
|
|
|
* |
709
|
|
|
* @return \Exception |
710
|
|
|
*/ |
711
|
|
|
protected function determineException(HttpClientException $exception) |
712
|
|
|
{ |
713
|
|
|
if (!empty($exception->getResponse()) && in_array($exception->getResponse()->getStatusCode(), [400, 409])) { |
714
|
|
|
return new BadRequest($exception->getResponse()); |
715
|
|
|
} |
716
|
|
|
|
717
|
|
|
return $exception; |
718
|
|
|
} |
719
|
|
|
} |
720
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.