Passed
Pull Request — master (#62)
by
unknown
13:26 queued 03:00
created

Client::getFile()   B

Complexity

Conditions 9
Paths 5

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 10.8889

Importance

Changes 4
Bugs 1 Features 0
Metric Value
eloc 20
c 4
b 1
f 0
dl 0
loc 30
ccs 15
cts 21
cp 0.7143
rs 8.0555
cc 9
nc 5
nop 1
crap 10.8889
1
<?php
2
3
namespace BackblazeB2;
4
5
use BackblazeB2\Exceptions\B2Exception;
6
use BackblazeB2\Exceptions\NotFoundException;
7
use BackblazeB2\Exceptions\ValidationException;
8
use BackblazeB2\Http\Client as HttpClient;
9
use Carbon\Carbon;
10
use GuzzleHttp\Exception\GuzzleException;
11
12
class Client
13
{
14
    private const B2_API_BASE_URL = 'https://api.backblazeb2.com';
15
    private const B2_API_V1 = '/b2api/v1/';
16
    protected $accountId;
17
    protected $applicationKey;
18
    protected $authToken;
19
    protected $apiUrl;
20
    protected $downloadUrl;
21
    protected $client;
22
    protected $reAuthTime;
23
    protected $authTimeoutSeconds;
24
25
    /**
26
     * Accepts the account ID, application key and an optional array of options.
27
     *
28
     * @param $accountId
29
     * @param $applicationKey
30
     * @param array $options
31
     *
32
     * @throws \Exception
33
     */
34 28
    public function __construct($accountId, $applicationKey, array $options = [])
35
    {
36 28
        $this->accountId = $accountId;
37 28
        $this->applicationKey = $applicationKey;
38
39 28
        $this->authTimeoutSeconds = 12 * 60 * 60; // 12 hour default
40 28
        if (isset($options['auth_timeout_seconds'])) {
41 1
            $this->authTimeoutSeconds = $options['auth_timeout_seconds'];
42
        }
43
44
        // set reauthorize time to force an authentication to take place
45 28
        $this->reAuthTime = Carbon::now('UTC')->subSeconds($this->authTimeoutSeconds * 2);
46
47 28
        $this->client = new HttpClient(['exceptions' => false]);
48 28
        if (isset($options['client'])) {
49 28
            $this->client = $options['client'];
50
        }
51 28
    }
52
53
    /**
54
     * Create a bucket with the given name and type.
55
     *
56
     * @param array $options
57
     *
58
     * @throws ValidationException
59
     * @throws GuzzleException     If the request fails.
60
     * @throws B2Exception         If the B2 server replies with an error.
61
     *
62
     * @return Bucket
63
     */
64 5
    public function createBucket(array $options)
65
    {
66 5
        if (!in_array($options['BucketType'], [Bucket::TYPE_PUBLIC, Bucket::TYPE_PRIVATE])) {
67 1
            throw new ValidationException(
68 1
                sprintf('Bucket type must be %s or %s', Bucket::TYPE_PRIVATE, Bucket::TYPE_PUBLIC)
69
            );
70
        }
71
72 4
        $response = $this->sendAuthorizedRequest('POST', 'b2_create_bucket', [
73 4
            'accountId'  => $this->accountId,
74 4
            'bucketName' => $options['BucketName'],
75 4
            'bucketType' => $options['BucketType'],
76
        ]);
77
78 3
        return new Bucket($response['bucketId'], $response['bucketName'], $response['bucketType']);
79
    }
80
81
    /**
82
     * Updates the type attribute of a bucket by the given ID.
83
     *
84
     * @param array $options
85
     *
86
     * @throws ValidationException
87
     * @throws GuzzleException     If the request fails.
88
     * @throws B2Exception         If the B2 server replies with an error.
89
     *
90
     * @return Bucket
91
     */
92 2
    public function updateBucket(array $options)
93
    {
94 2
        if (!in_array($options['BucketType'], [Bucket::TYPE_PUBLIC, Bucket::TYPE_PRIVATE])) {
95
            throw new ValidationException(
96
                sprintf('Bucket type must be %s or %s', Bucket::TYPE_PRIVATE, Bucket::TYPE_PUBLIC)
97
            );
98
        }
99
100 2
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
101
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
102
        }
103
104 2
        $response = $this->sendAuthorizedRequest('POST', 'b2_update_bucket', [
105 2
            'accountId'  => $this->accountId,
106 2
            'bucketId'   => $options['BucketId'],
107 2
            'bucketType' => $options['BucketType'],
108
        ]);
109
110 2
        return new Bucket($response['bucketId'], $response['bucketName'], $response['bucketType']);
111
    }
112
113
    /**
114
     * Returns a list of bucket objects representing the buckets on the account.
115
     *
116
     * @throws GuzzleException If the request fails.
117
     * @throws B2Exception     If the B2 server replies with an error.
118
     *
119
     * @return array
120
     */
121 2
    public function listBuckets()
122
    {
123 2
        $buckets = [];
124
125 2
        $response = $this->sendAuthorizedRequest('POST', 'b2_list_buckets', [
126 2
            'accountId' => $this->accountId,
127
        ]);
128
129 2
        foreach ($response['buckets'] as $bucket) {
130 1
            $buckets[] = new Bucket($bucket['bucketId'], $bucket['bucketName'], $bucket['bucketType']);
131
        }
132
133 2
        return $buckets;
134
    }
135
136
    /**
137
     * Deletes the bucket identified by its ID.
138
     *
139
     * @param array $options
140
     *
141
     * @throws GuzzleException If the request fails.
142
     * @throws B2Exception     If the B2 server replies with an error.
143
     *
144
     * @return bool
145
     */
146 3
    public function deleteBucket(array $options)
147
    {
148 3
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
149
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
150
        }
151
152 3
        $this->sendAuthorizedRequest('POST', 'b2_delete_bucket', [
153 3
            'accountId' => $this->accountId,
154 3
            'bucketId'  => $options['BucketId'],
155
        ]);
156
157 1
        return true;
158
    }
159
160
    /**
161
     * Uploads a file to a bucket and returns a File object.
162
     *
163
     * @param array $options
164
     *
165
     * @throws GuzzleException If the request fails.
166
     * @throws B2Exception     If the B2 server replies with an error.
167
     *
168
     * @return File
169
     */
170 3
    public function upload(array $options)
171
    {
172
        // Clean the path if it starts with /.
173 3
        if (substr($options['FileName'], 0, 1) === '/') {
174
            $options['FileName'] = ltrim($options['FileName'], '/');
175
        }
176
177 3
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
178
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
179
        }
180
181
        // Retrieve the URL that we should be uploading to.
182
183 3
        $response = $this->sendAuthorizedRequest('POST', 'b2_get_upload_url', [
184 3
            'bucketId' => $options['BucketId'],
185
        ]);
186
187 3
        $uploadEndpoint = $response['uploadUrl'];
188 3
        $uploadAuthToken = $response['authorizationToken'];
189
190 3
        if (is_resource($options['Body'])) {
191
            // We need to calculate the file's hash incrementally from the stream.
192 1
            $context = hash_init('sha1');
193 1
            hash_update_stream($context, $options['Body']);
194 1
            $hash = hash_final($context);
195
196
            // Similarly, we have to use fstat to get the size of the stream.
197 1
            $size = fstat($options['Body'])['size'];
198
199
            // Rewind the stream before passing it to the HTTP client.
200 1
            rewind($options['Body']);
201
        } else {
202
            // We've been given a simple string body, it's super simple to calculate the hash and size.
203 2
            $hash = sha1($options['Body']);
204 2
            $size = strlen($options['Body']);
205
        }
206
207 3
        if (!isset($options['FileLastModified'])) {
208 2
            $options['FileLastModified'] = round(microtime(true) * 1000);
209
        }
210
211 3
        if (!isset($options['FileContentType'])) {
212 2
            $options['FileContentType'] = 'b2/x-auto';
213
        }
214
215 3
        $response = $this->client->guzzleRequest('POST', $uploadEndpoint, [
216
            'headers' => [
217 3
                'Authorization'                      => $uploadAuthToken,
218 3
                'Content-Type'                       => $options['FileContentType'],
219 3
                'Content-Length'                     => $size,
220 3
                'X-Bz-File-Name'                     => $options['FileName'],
221 3
                'X-Bz-Content-Sha1'                  => $hash,
222 3
                'X-Bz-Info-src_last_modified_millis' => $options['FileLastModified'],
223
            ],
224 3
            'body' => $options['Body'],
225
        ]);
226
227 3
        return new File(
228 3
            $response['fileId'],
229 3
            $response['fileName'],
230 3
            $response['contentSha1'],
231 3
            $response['contentLength'],
232 3
            $response['contentType'],
233 3
            $response['fileInfo']
234
        );
235
    }
236
237
    /**
238
     * Download a file from a B2 bucket.
239
     *
240
     * @param array $options
241
     *
242
     * @return bool
243
     */
244 6
    public function download(array $options)
245
    {
246 6
        $requestUrl = null;
247
        $requestOptions = [
248 6
            'headers' => [
249 6
                'Authorization' => $this->authToken,
250
            ],
251 6
            'sink' => isset($options['SaveAs']) ? $options['SaveAs'] : null,
252
        ];
253
254 6
        if (isset($options['FileId'])) {
255 3
            $requestOptions['query'] = ['fileId' => $options['FileId']];
256 3
            $requestUrl = $this->downloadUrl.'/b2api/v1/b2_download_file_by_id';
257
        } else {
258 3
            if (!isset($options['BucketName']) && isset($options['BucketId'])) {
259
                $options['BucketName'] = $this->getBucketNameFromId($options['BucketId']);
260
            }
261
262 3
            $requestUrl = sprintf('%s/file/%s/%s', $this->downloadUrl, $options['BucketName'], $options['FileName']);
263
        }
264
265 6
        $this->authorizeAccount();
266
267 6
        $response = $this->client->guzzleRequest('GET', $requestUrl, $requestOptions, false);
268
269 4
        return isset($options['SaveAs']) ? true : $response;
270
    }
271
272
    /**
273
     * Retrieve a collection of File objects representing the files stored inside a bucket.
274
     *
275
     * @param array $options
276
     *
277
     * @throws GuzzleException If the request fails.
278
     * @throws B2Exception     If the B2 server replies with an error.
279
     *
280
     * @return array
281
     */
282 2
    public function listFiles(array $options)
283
    {
284
        // if FileName is set, we only attempt to retrieve information about that single file.
285 2
        $fileName = !empty($options['FileName']) ? $options['FileName'] : null;
286
287 2
        $nextFileName = null;
288 2
        $maxFileCount = 1000;
289
290 2
        $prefix = isset($options['Prefix']) ? $options['Prefix'] : '';
291 2
        $delimiter = isset($options['Delimiter']) ? $options['Delimiter'] : null;
292
293 2
        $files = [];
294
295 2
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
296
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
297
        }
298
299 2
        if ($fileName) {
300
            $nextFileName = $fileName;
301
            $maxFileCount = 1;
302
        }
303
304 2
        $this->authorizeAccount();
305
306
        // B2 returns, at most, 1000 files per "page". Loop through the pages and compile an array of File objects.
307 2
        while (true) {
308 2
            $response = $this->sendAuthorizedRequest('POST', 'b2_list_file_names', [
309 2
                'bucketId'      => $options['BucketId'],
310 2
                'startFileName' => $nextFileName,
311 2
                'maxFileCount'  => $maxFileCount,
312 2
                'prefix'        => $prefix,
313 2
                'delimiter'     => $delimiter,
314
            ]);
315
316 2
            foreach ($response['files'] as $file) {
317
                // if we have a file name set, only retrieve information if the file name matches
318 1
                if (!$fileName || ($fileName === $file['fileName'])) {
319 1
                    $files[] = new File($file['fileId'], $file['fileName'], null, $file['size']);
320
                }
321
            }
322
323 2
            if ($fileName || $response['nextFileName'] === null) {
324
                // We've got all the files - break out of loop.
325 2
                break;
326
            }
327
328 1
            $nextFileName = $response['nextFileName'];
329
        }
330
331 2
        return $files;
332
    }
333
334
    /**
335
     * Test whether a file exists in B2 for the given bucket.
336
     *
337
     * @param array $options
338
     *
339
     * @return bool
340
     */
341
    public function fileExists(array $options)
342
    {
343
        $files = $this->listFiles($options);
344
345
        return !empty($files);
346
    }
347
348
    /**
349
     * Returns a single File object representing a file stored on B2.
350
     *
351
     * @param array $options
352
     *
353
     * @throws GuzzleException
354
     * @throws NotFoundException If no file id was provided and BucketName + FileName does not resolve to a file, a NotFoundException is thrown.
355
     * @throws GuzzleException   If the request fails.
356
     * @throws B2Exception       If the B2 server replies with an error.
357
     *
358
     * @return File
359
     */
360 4
    public function getFile(array $options)
361
    {
362 4
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
363
            $options['FileId'] = $this->getFileIdFromBucketAndFileName($options['BucketName'], $options['FileName']);
364
365
            if (!$options['FileId']) {
366
                throw new NotFoundException();
367
            }
368 4
        } elseif (!isset($options['FileId']) && isset($options['BucketId']) && isset($options['FileName'])) {
369
            $options['FileId'] = $this->getFileIdFromBucketIdAndFileName($options['BucketId'], $options['FileName']);
370
371
            if (!$options['FileId']) {
372
                throw new NotFoundException();
373
            }
374
        }
375
376 4
        $response = $this->sendAuthorizedRequest('POST', 'b2_get_file_info', [
377 4
            'fileId' => $options['FileId'],
378
        ]);
379
380 3
        return new File(
381 3
            $response['fileId'],
382 3
            $response['fileName'],
383 3
            $response['contentSha1'],
384 3
            $response['contentLength'],
385 3
            $response['contentType'],
386 3
            $response['fileInfo'],
387 3
            $response['bucketId'],
388 3
            $response['action'],
389 3
            $response['uploadTimestamp']
390
        );
391
    }
392
393
    /**
394
     * Deletes the file identified by ID from Backblaze B2.
395
     *
396
     * @param array $options
397
     *
398
     * @throws GuzzleException
399
     * @throws NotFoundException
400
     * @throws GuzzleException   If the request fails.
401
     * @throws B2Exception       If the B2 server replies with an error.
402
     *
403
     * @return bool
404
     */
405 3
    public function deleteFile(array $options)
406
    {
407 3
        if (!isset($options['FileName'])) {
408 2
            $file = $this->getFile($options);
409
410 2
            $options['FileName'] = $file->getName();
411
        }
412
413 3
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
414
            $file = $this->getFile($options);
415
416
            $options['FileId'] = $file->getId();
417
        }
418
419 3
        $this->sendAuthorizedRequest('POST', 'b2_delete_file_version', [
420 3
            'fileName' => $options['FileName'],
421 3
            'fileId'   => $options['FileId'],
422
        ]);
423
424 2
        return true;
425
    }
426
427
    /**
428
     * Authorize the B2 account in order to get an auth token and API/download URLs.
429
     */
430 27
    protected function authorizeAccount()
431
    {
432 27
        if (Carbon::now('UTC')->timestamp < $this->reAuthTime->timestamp) {
433 4
            return;
434
        }
435
436 27
        $response = $this->client->guzzleRequest('GET', self::B2_API_BASE_URL.self::B2_API_V1.'/b2_authorize_account', [
437 27
            'auth' => [$this->accountId, $this->applicationKey],
438
        ]);
439
440 27
        $this->authToken = $response['authorizationToken'];
441 27
        $this->apiUrl = $response['apiUrl'].self::B2_API_V1;
442 27
        $this->downloadUrl = $response['downloadUrl'];
443 27
        $this->reAuthTime = Carbon::now('UTC');
444 27
        $this->reAuthTime->addSeconds($this->authTimeoutSeconds);
445 27
    }
446
447
    /**
448
     * Maps the provided bucket name to the appropriate bucket ID.
449
     *
450
     * @param $name
451
     *
452
     * @return mixed
453
     */
454
    protected function getBucketIdFromName($name)
455
    {
456
        $buckets = $this->listBuckets();
457
458
        foreach ($buckets as $bucket) {
459
            if ($bucket->getName() === $name) {
460
                return $bucket->getId();
461
            }
462
        }
463
    }
464
465
    /**
466
     * Maps the provided bucket ID to the appropriate bucket name.
467
     *
468
     * @param $id
469
     *
470
     * @return mixed
471
     */
472
    protected function getBucketNameFromId($id)
473
    {
474
        $buckets = $this->listBuckets();
475
476
        foreach ($buckets as $bucket) {
477
            if ($bucket->getId() === $id) {
478
                return $bucket->getName();
479
            }
480
        }
481
    }
482
483
    /**
484
     * @param $bucketName
485
     * @param $fileName
486
     *
487
     * @return mixed
488
     */
489
    protected function getFileIdFromBucketAndFileName($bucketName, $fileName)
490
    {
491
        $files = $this->listFiles([
492
            'BucketName' => $bucketName,
493
            'FileName'   => $fileName,
494
        ]);
495
496
        foreach ($files as $file) {
497
            if ($file->getName() === $fileName) {
498
                return $file->getId();
499
            }
500
        }
501
    }
502
503
    /**
504
     * @param $bucketId
505
     * @param $fileName
506
     *
507
     * @return mixed
508
     */
509
    protected function getFileIdFromBucketIdAndFileName($bucketId, $fileName)
510
    {
511
        $files = $this->listFiles([
512
            'BucketId' => $bucketId,
513
            'FileName' => $fileName,
514
        ]);
515
516
        foreach ($files as $file) {
517
            if ($file->getName() === $fileName) {
518
                return $file->getId();
519
            }
520
        }
521
    }
522
523
    /**
524
     * Uploads a large file using b2 large file procedure.
525
     *
526
     * @param array $options
527
     *
528
     * @return File
529
     */
530
    public function uploadLargeFile(array $options)
531
    {
532
        if (substr($options['FileName'], 0, 1) === '/') {
533
            $options['FileName'] = ltrim($options['FileName'], '/');
534
        }
535
536
        //if last char of path is not a "/" then add a "/"
537
        if (substr($options['FilePath'], -1) != '/') {
538
            $options['FilePath'] = $options['FilePath'].'/';
539
        }
540
541
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
542
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
543
        }
544
545
        if (!isset($options['FileContentType'])) {
546
            $options['FileContentType'] = 'b2/x-auto';
547
        }
548
549
        $this->authorizeAccount();
550
551
        // 1) b2_start_large_file, (returns fileId)
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
552
        $start = $this->startLargeFile($options['FileName'], $options['FileContentType'], $options['BucketId']);
553
554
        // 2) b2_get_upload_part_url for each thread uploading (takes fileId)
555
        $url = $this->getUploadPartUrl($start['fileId']);
556
557
        // 3) b2_upload_part for each part of the file
558
        $parts = $this->uploadParts($options['FilePath'].$options['FileName'], $url['uploadUrl'], $url['authorizationToken']);
559
560
        $sha1s = [];
561
562
        foreach ($parts as $part) {
563
            $sha1s[] = $part['contentSha1'];
564
        }
565
566
        // 4) b2_finish_large_file.
567
        return $this->finishLargeFile($start['fileId'], $sha1s);
568
    }
569
570
    /**
571
     * Starts the large file upload process.
572
     *
573
     * @param $fileName
574
     * @param $contentType
575
     * @param $bucketId
576
     *
577
     * @throws GuzzleException If the request fails.
578
     * @throws B2Exception     If the B2 server replies with an error.
579
     *
580
     * @return mixed
581
     */
582
    protected function startLargeFile($fileName, $contentType, $bucketId)
583
    {
584
        return $this->sendAuthorizedRequest('POST', 'b2_start_large_file', [
585
            'fileName'      => $fileName,
586
            'contentType'   => $contentType,
587
            'bucketId'      => $bucketId,
588
        ]);
589
    }
590
591
    /**
592
     * Gets the url for the next large file part upload.
593
     *
594
     * @param $fileId
595
     *
596
     * @throws GuzzleException If the request fails.
597
     * @throws B2Exception     If the B2 server replies with an error.
598
     *
599
     * @return mixed
600
     */
601
    protected function getUploadPartUrl($fileId)
602
    {
603
        return $this->sendAuthorizedRequest('POST', 'b2_get_upload_part_url', [
604
            'fileId' => $fileId,
605
        ]);
606
    }
607
608
    /**
609
     * Uploads the file as "parts" of 100MB each.
610
     *
611
     * @param $filePath
612
     * @param $uploadUrl
613
     * @param $largeFileAuthToken
614
     *
615
     * @return array
616
     */
617
    protected function uploadParts($filePath, $uploadUrl, $largeFileAuthToken)
618
    {
619
        $return = [];
620
621
        $minimum_part_size = 100 * (1000 * 1000);
622
623
        $local_file_size = filesize($filePath);
624
        $total_bytes_sent = 0;
625
        $bytes_sent_for_part = $minimum_part_size;
626
        $sha1_of_parts = [];
627
        $part_no = 1;
628
        $file_handle = fopen($filePath, 'r');
629
630
        while ($total_bytes_sent < $local_file_size) {
631
632
            // Determine the number of bytes to send based on the minimum part size
633
            if (($local_file_size - $total_bytes_sent) < $minimum_part_size) {
634
                $bytes_sent_for_part = ($local_file_size - $total_bytes_sent);
635
            }
636
637
            // Get a sha1 of the part we are going to send
638
            fseek($file_handle, $total_bytes_sent);
0 ignored issues
show
Bug introduced by
It seems like $file_handle can also be of type false; however, parameter $handle of fseek() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

638
            fseek(/** @scrutinizer ignore-type */ $file_handle, $total_bytes_sent);
Loading history...
639
            $data_part = fread($file_handle, $bytes_sent_for_part);
0 ignored issues
show
Bug introduced by
It seems like $file_handle can also be of type false; however, parameter $handle of fread() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

639
            $data_part = fread(/** @scrutinizer ignore-type */ $file_handle, $bytes_sent_for_part);
Loading history...
640
            array_push($sha1_of_parts, sha1($data_part));
641
            fseek($file_handle, $total_bytes_sent);
642
643
            $response = $this->client->guzzleRequest('POST', $uploadUrl, [
644
                'headers' => [
645
                    'Authorization'                      => $largeFileAuthToken,
646
                    'Content-Length'                     => $bytes_sent_for_part,
647
                    'X-Bz-Part-Number'                   => $part_no,
648
                    'X-Bz-Content-Sha1'                  => $sha1_of_parts[$part_no - 1],
649
                ],
650
                'body' => $data_part,
651
            ]);
652
653
            $return[] = $response;
654
655
            // Prepare for the next iteration of the loop
656
            $part_no++;
657
            $total_bytes_sent = $bytes_sent_for_part + $total_bytes_sent;
658
        }
659
660
        fclose($file_handle);
0 ignored issues
show
Bug introduced by
It seems like $file_handle can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

660
        fclose(/** @scrutinizer ignore-type */ $file_handle);
Loading history...
661
662
        return $return;
663
    }
664
665
    /**
666
     * Finishes the large file upload procedure.
667
     *
668
     * @param       $fileId
669
     * @param array $sha1s
670
     *
671
     * @throws GuzzleException If the request fails.
672
     * @throws B2Exception     If the B2 server replies with an error.
673
     *
674
     * @return File
675
     */
676
    protected function finishLargeFile($fileId, array $sha1s)
677
    {
678
        $response = $this->sendAuthorizedRequest('POST', 'b2_finish_large_file', [
679
            'fileId'        => $fileId,
680
            'partSha1Array' => $sha1s,
681
        ]);
682
683
        return new File(
684
            $response['fileId'],
685
            $response['fileName'],
686
            $response['contentSha1'],
687
            $response['contentLength'],
688
            $response['contentType'],
689
            $response['fileInfo'],
690
            $response['bucketId'],
691
            $response['action'],
692
            $response['uploadTimestamp']
693
        );
694
    }
695
696
    /**
697
     * Sends a authorized request to b2 API.
698
     *
699
     * @param string $method
700
     * @param string $route
701
     * @param array  $json
702
     *
703
     * @throws GuzzleException If the request fails.
704
     * @throws B2Exception     If the B2 server replies with an error.
705
     *
706
     * @return mixed
707
     */
708 21
    protected function sendAuthorizedRequest($method, $route, $json = [])
709
    {
710 21
        $this->authorizeAccount();
711
712 21
        return $this->client->guzzleRequest($method, $this->apiUrl.$route, [
713
            'headers' => [
714 21
                'Authorization' => $this->authToken,
715
            ],
716 21
            'json' => $json,
717
        ]);
718
    }
719
}
720