Completed
Pull Request — master (#19)
by Ramesh
03:22
created

Client::uploadLargeFile()   B

Complexity

Conditions 7
Paths 32

Size

Total Lines 36
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 36
ccs 0
cts 16
cp 0
rs 8.8333
c 0
b 0
f 0
cc 7
nc 32
nop 1
crap 56
1
<?php
2
3
namespace BackblazeB2;
4
5
use BackblazeB2\Exceptions\NotFoundException;
6
use BackblazeB2\Exceptions\ValidationException;
7
use BackblazeB2\Http\Client as HttpClient;
8
9
class Client
10
{
11
    protected $accountId;
12
    protected $applicationKey;
13
14
    protected $authToken;
15
    protected $apiUrl;
16
    protected $downloadUrl;
17
18
    protected $client;
19
20
    /**
21
     * Client constructor. Accepts the account ID, application key and an optional array of options.
22
     *
23
     * @param $accountId
24
     * @param $applicationKey
25
     * @param array $options
26
     */
27 27
    public function __construct($accountId, $applicationKey, array $options = [])
28
    {
29 27
        $this->accountId = $accountId;
30 27
        $this->applicationKey = $applicationKey;
31
32 27
        if (isset($options['client'])) {
33 27
            $this->client = $options['client'];
34
        } else {
35
            $this->client = new HttpClient(['exceptions' => false]);
36
        }
37
38 27
        $this->authorizeAccount();
39 27
    }
40
41
    /**
42
     * Create a bucket with the given name and type.
43
     *
44
     * @param array $options
45
     *
46
     * @throws ValidationException
47
     *
48
     * @return Bucket
49
     */
50 4
    public function createBucket(array $options)
51
    {
52 4
        if (!in_array($options['BucketType'], [Bucket::TYPE_PUBLIC, Bucket::TYPE_PRIVATE])) {
53 1
            throw new ValidationException(
54 1
                sprintf('Bucket type must be %s or %s', Bucket::TYPE_PRIVATE, Bucket::TYPE_PUBLIC)
55
            );
56
        }
57
58 3
        $response = $this->client->request('POST', $this->apiUrl.'/b2_create_bucket', [
59
            'headers' => [
60 3
                'Authorization' => $this->authToken,
61
            ],
62
            'json' => [
63 3
                'accountId'  => $this->accountId,
64 3
                'bucketName' => $options['BucketName'],
65 3
                'bucketType' => $options['BucketType'],
66
            ],
67
        ]);
68
69 2
        return new Bucket($response['bucketId'], $response['bucketName'], $response['bucketType']);
70
    }
71
72
    /**
73
     * Updates the type attribute of a bucket by the given ID.
74
     *
75
     * @param array $options
76
     *
77
     * @throws ValidationException
78
     *
79
     * @return Bucket
80
     */
81 2
    public function updateBucket(array $options)
82
    {
83 2
        if (!in_array($options['BucketType'], [Bucket::TYPE_PUBLIC, Bucket::TYPE_PRIVATE])) {
84
            throw new ValidationException(
85
                sprintf('Bucket type must be %s or %s', Bucket::TYPE_PRIVATE, Bucket::TYPE_PUBLIC)
86
            );
87
        }
88
89 2
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
90
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
91
        }
92
93 2
        $response = $this->client->request('POST', $this->apiUrl.'/b2_update_bucket', [
94
            'headers' => [
95 2
                'Authorization' => $this->authToken,
96
            ],
97
            'json' => [
98 2
                'accountId'  => $this->accountId,
99 2
                'bucketId'   => $options['BucketId'],
100 2
                'bucketType' => $options['BucketType'],
101
            ],
102
        ]);
103
104 2
        return new Bucket($response['bucketId'], $response['bucketName'], $response['bucketType']);
105
    }
106
107
    /**
108
     * Returns a list of bucket objects representing the buckets on the account.
109
     *
110
     * @return array
111
     */
112 2
    public function listBuckets()
113
    {
114 2
        $buckets = [];
115
116 2
        $response = $this->client->request('POST', $this->apiUrl.'/b2_list_buckets', [
117
            'headers' => [
118 2
                'Authorization' => $this->authToken,
119
            ],
120
            'json' => [
121 2
                'accountId' => $this->accountId,
122
            ],
123
        ]);
124
125 2
        foreach ($response['buckets'] as $bucket) {
126 1
            $buckets[] = new Bucket($bucket['bucketId'], $bucket['bucketName'], $bucket['bucketType']);
127
        }
128
129 2
        return $buckets;
130
    }
131
132
    /**
133
     * Deletes the bucket identified by its ID.
134
     *
135
     * @param array $options
136
     *
137
     * @return bool
138
     */
139 3
    public function deleteBucket(array $options)
140
    {
141 3
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
142
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
143
        }
144
145 3
        $this->client->request('POST', $this->apiUrl.'/b2_delete_bucket', [
146
            'headers' => [
147 3
                'Authorization' => $this->authToken,
148
            ],
149
            'json' => [
150 3
                'accountId' => $this->accountId,
151 3
                'bucketId'  => $options['BucketId'],
152
            ],
153
        ]);
154
155 1
        return true;
156
    }
157
158
    /**
159
     * Uploads a file to a bucket and returns a File object.
160
     *
161
     * @param array $options
162
     *
163
     * @return File
164
     */
165 3
    public function upload(array $options)
166
    {
167
        // Clean the path if it starts with /.
168 3
        if (substr($options['FileName'], 0, 1) === '/') {
169
            $options['FileName'] = ltrim($options['FileName'], '/');
170
        }
171
172 3
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
173
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
174
        }
175
176
        // Retrieve the URL that we should be uploading to.
177 3
        $response = $this->client->request('POST', $this->apiUrl.'/b2_get_upload_url', [
178
            'headers' => [
179 3
                'Authorization' => $this->authToken,
180
            ],
181
            'json' => [
182 3
                'bucketId' => $options['BucketId'],
183
            ],
184
        ]);
185
186 3
        $uploadEndpoint = $response['uploadUrl'];
187 3
        $uploadAuthToken = $response['authorizationToken'];
188
189 3
        if (is_resource($options['Body'])) {
190
            // We need to calculate the file's hash incrementally from the stream.
191 1
            $context = hash_init('sha1');
192 1
            hash_update_stream($context, $options['Body']);
193 1
            $hash = hash_final($context);
194
195
            // Similarly, we have to use fstat to get the size of the stream.
196 1
            $size = fstat($options['Body'])['size'];
197
198
            // Rewind the stream before passing it to the HTTP client.
199 1
            rewind($options['Body']);
200
        } else {
201
            // We've been given a simple string body, it's super simple to calculate the hash and size.
202 2
            $hash = sha1($options['Body']);
203 2
            $size = strlen($options['Body']);
204
        }
205
206 3
        if (!isset($options['FileLastModified'])) {
207 2
            $options['FileLastModified'] = round(microtime(true) * 1000);
208
        }
209
210 3
        if (!isset($options['FileContentType'])) {
211 2
            $options['FileContentType'] = 'b2/x-auto';
212
        }
213
214 3
        $response = $this->client->request('POST', $uploadEndpoint, [
215
            'headers' => [
216 3
                'Authorization'                      => $uploadAuthToken,
217 3
                'Content-Type'                       => $options['FileContentType'],
218 3
                'Content-Length'                     => $size,
219 3
                'X-Bz-File-Name'                     => $options['FileName'],
220 3
                'X-Bz-Content-Sha1'                  => $hash,
221 3
                'X-Bz-Info-src_last_modified_millis' => $options['FileLastModified'],
222
            ],
223 3
            'body' => $options['Body'],
224
        ]);
225
226 3
        return new File(
227 3
            $response['fileId'],
228 3
            $response['fileName'],
229 3
            $response['contentSha1'],
230 3
            $response['contentLength'],
231 3
            $response['contentType'],
232 3
            $response['fileInfo']
233
        );
234
    }
235
236
    /**
237
     * Download a file from a B2 bucket.
238
     *
239
     * @param array $options
240
     *
241
     * @return bool|mixed|string
242
     */
243 6
    public function download(array $options)
244
    {
245 6
        $requestUrl = null;
246
        $requestOptions = [
247 6
            'headers' => [
248 6
                'Authorization' => $this->authToken,
249
            ],
250 6
            'sink' => isset($options['SaveAs']) ? $options['SaveAs'] : null,
251
        ];
252
253 6
        if (isset($options['FileId'])) {
254 3
            $requestOptions['query'] = ['fileId' => $options['FileId']];
255 3
            $requestUrl = $this->downloadUrl.'/b2api/v1/b2_download_file_by_id';
256
        } else {
257 3
            if (!isset($options['BucketName']) && isset($options['BucketId'])) {
258
                $options['BucketName'] = $this->getBucketNameFromId($options['BucketId']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketName'] is correct as $this->getBucketNameFromId($options['BucketId']) targeting BackblazeB2\Client::getBucketNameFromId() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
259
            }
260
261 3
            $requestUrl = sprintf('%s/file/%s/%s', $this->downloadUrl, $options['BucketName'], $options['FileName']);
262
        }
263
264 6
        $response = $this->client->request('GET', $requestUrl, $requestOptions, false);
265
266 4
        return isset($options['SaveAs']) ? true : $response;
267
    }
268
269
    /**
270
     * Retrieve a collection of File objects representing the files stored inside a bucket.
271
     *
272
     * @param array $options
273
     *
274
     * @return array
275
     */
276 2
    public function listFiles(array $options)
277
    {
278
        // if FileName is set, we only attempt to retrieve information about that single file.
279 2
        $fileName = !empty($options['FileName']) ? $options['FileName'] : null;
280
281 2
        $nextFileName = null;
282 2
        $maxFileCount = 1000;
283 2
        $files = [];
284
285 2
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
286
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
287
        }
288
289 2
        if ($fileName) {
290
            $nextFileName = $fileName;
291
            $maxFileCount = 1;
292
        }
293
294
        // B2 returns, at most, 1000 files per "page". Loop through the pages and compile an array of File objects.
295 2
        while (true) {
296 2
            $response = $this->client->request('POST', $this->apiUrl.'/b2_list_file_names', [
297
                'headers' => [
298 2
                    'Authorization' => $this->authToken,
299
                ],
300
                'json' => [
301 2
                    'bucketId'      => $options['BucketId'],
302 2
                    'startFileName' => $nextFileName,
303 2
                    'maxFileCount'  => $maxFileCount,
304
                ],
305
            ]);
306
307 2
            foreach ($response['files'] as $file) {
308
                // if we have a file name set, only retrieve information if the file name matches
309 1
                if (!$fileName || ($fileName === $file['fileName'])) {
310 1
                    $files[] = new File($file['fileId'], $file['fileName'], null, $file['size']);
311
                }
312
            }
313
314 2
            if ($fileName || $response['nextFileName'] === null) {
315
                // We've got all the files - break out of loop.
316 2
                break;
317
            }
318
319 1
            $nextFileName = $response['nextFileName'];
320
        }
321
322 2
        return $files;
323
    }
324
325
    /**
326
     * Test whether a file exists in B2 for the given bucket.
327
     *
328
     * @param array $options
329
     *
330
     * @return bool
331
     */
332
    public function fileExists(array $options)
333
    {
334
        $files = $this->listFiles($options);
335
336
        return !empty($files);
337
    }
338
339
    /**
340
     * Returns a single File object representing a file stored on B2.
341
     *
342
     * @param array $options
343
     *
344
     * @throws NotFoundException If no file id was provided and BucketName + FileName does not resolve to a file, a NotFoundException is thrown.
345
     *
346
     * @return File
347
     */
348 4
    public function getFile(array $options)
349
    {
350 4
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
351
            $options['FileId'] = $this->getFileIdFromBucketAndFileName($options['BucketName'], $options['FileName']);
352
353
            if (!$options['FileId']) {
354
                throw new NotFoundException();
355
            }
356
        }
357
358 4
        $response = $this->client->request('POST', $this->apiUrl.'/b2_get_file_info', [
359
            'headers' => [
360 4
                'Authorization' => $this->authToken,
361
            ],
362
            'json' => [
363 4
                'fileId' => $options['FileId'],
364
            ],
365
        ]);
366
367 3
        return new File(
368 3
            $response['fileId'],
369 3
            $response['fileName'],
370 3
            $response['contentSha1'],
371 3
            $response['contentLength'],
372 3
            $response['contentType'],
373 3
            $response['fileInfo'],
374 3
            $response['bucketId'],
375 3
            $response['action'],
376 3
            $response['uploadTimestamp']
377
        );
378
    }
379
380
    /**
381
     * Deletes the file identified by ID from Backblaze B2.
382
     *
383
     * @param array $options
384
     *
385
     * @return bool
386
     */
387 3
    public function deleteFile(array $options)
388
    {
389 3
        if (!isset($options['FileName'])) {
390 2
            $file = $this->getFile($options);
391
392 2
            $options['FileName'] = $file->getName();
393
        }
394
395 3
        if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
396
            $file = $this->getFile($options);
397
398
            $options['FileId'] = $file->getId();
399
        }
400
401 3
        $this->client->request('POST', $this->apiUrl.'/b2_delete_file_version', [
402
            'headers' => [
403 3
                'Authorization' => $this->authToken,
404
            ],
405
            'json' => [
406 3
                'fileName' => $options['FileName'],
407 3
                'fileId'   => $options['FileId'],
408
            ],
409
        ]);
410
411 2
        return true;
412
    }
413
414
    /**
415
     * Authorize the B2 account in order to get an auth token and API/download URLs.
416
     *
417
     * @throws \Exception
418
     */
419 27
    protected function authorizeAccount()
420
    {
421 27
        $response = $this->client->request('GET', 'https://api.backblazeb2.com/b2api/v1/b2_authorize_account', [
422 27
            'auth' => [$this->accountId, $this->applicationKey],
423
        ]);
424
425 27
        $this->authToken = $response['authorizationToken'];
426 27
        $this->apiUrl = $response['apiUrl'].'/b2api/v1';
427 27
        $this->downloadUrl = $response['downloadUrl'];
428 27
    }
429
430
    /**
431
     * Maps the provided bucket name to the appropriate bucket ID.
432
     *
433
     * @param $name
434
     *
435
     * @return null
436
     */
437
    protected function getBucketIdFromName($name)
438
    {
439
        $buckets = $this->listBuckets();
440
441
        foreach ($buckets as $bucket) {
442
            if ($bucket->getName() === $name) {
443
                return $bucket->getId();
444
            }
445
        }
446
    }
447
448
    /**
449
     * Maps the provided bucket ID to the appropriate bucket name.
450
     *
451
     * @param $id
452
     *
453
     * @return null
454
     */
455
    protected function getBucketNameFromId($id)
456
    {
457
        $buckets = $this->listBuckets();
458
459
        foreach ($buckets as $bucket) {
460
            if ($bucket->getId() === $id) {
461
                return $bucket->getName();
462
            }
463
        }
464
    }
465
466
    protected function getFileIdFromBucketAndFileName($bucketName, $fileName)
467
    {
468
        $files = $this->listFiles([
469
            'BucketName' => $bucketName,
470
            'FileName'   => $fileName,
471
        ]);
472
473
        foreach ($files as $file) {
474
            if ($file->getName() === $fileName) {
475
                return $file->getId();
476
            }
477
        }
478
    }
479
480
    /**
481
     * Uploads a large file using b2 large file proceedure.
482
     *
483
     * @param array $options
484
     *
485
     * @return \BackblazeB2\File
486
     */
487
    public function uploadLargeFile(array $options)
488
    {
489
        if (substr($options['FileName'], 0, 1) === '/') {
490
            $options['FileName'] = ltrim($options['FileName'], '/');
491
        }
492
493
        //if last char of path is not a "/" then add a "/"
494
        if (substr($options['FilePath'], -1) != '/') {
495
            $options['FilePath'] = $options['FilePath'].'/';
496
        }
497
498
        if (!isset($options['BucketId']) && isset($options['BucketName'])) {
499
            $options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options['BucketId'] is correct as $this->getBucketIdFromNa...$options['BucketName']) targeting BackblazeB2\Client::getBucketIdFromName() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
500
        }
501
502
        if (!isset($options['FileContentType'])) {
503
            $options['FileContentType'] = 'b2/x-auto';
504
        }
505
506
        // 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...
507
        $start = $this->startLargeFile($options['FileName'], $options['FileContentType'], $options['BucketId']);
508
509
        // 2) b2_get_upload_part_url for each thread uploading (takes fileId)
510
        $url = $this->getUploadPartUrl($start['fileId']);
511
512
        // 3) b2_upload_part for each part of the file
513
        $parts = $this->uploadParts($options['FilePath'].$options['FileName'], $url['uploadUrl'], $url['authorizationToken']);
514
515
        $sha1s = [];
516
517
        foreach ($parts as $part) {
518
            $sha1s[] = $part['contentSha1'];
519
        }
520
521
        // 4) b2_finish_large_file.
522
        return $this->finishLargeFile($start['fileId'], $sha1s);
523
    }
524
525
    /**
526
     * starts the large file upload process.
527
     *
528
     * @param $fileName
529
     * @param $contentType
530
     * @param $bucketId
531
     *
532
     * @return array
533
     */
534
    protected function startLargeFile($fileName, $contentType, $bucketId)
535
    {
536
        $response = $this->client->request('POST', $this->apiUrl.'/b2_start_large_file', [
537
            'headers' => [
538
                'Authorization' => $this->authToken,
539
            ],
540
            'json' => [
541
                'fileName'      => $fileName,
542
                'contentType'   => $contentType,
543
                'bucketId'      => $bucketId,
544
            ],
545
        ]);
546
547
        return $response;
548
    }
549
550
    /**
551
     * gets the url for the next large file part upload.
552
     *
553
     * @param $fileId
554
     *
555
     * @return array
556
     */
557
    protected function getUploadPartUrl($fileId)
558
    {
559
        $response = $this->client->request('POST', $this->apiUrl.'/b2_get_upload_part_url', [
560
            'headers' => [
561
                'Authorization' => $this->authToken,
562
            ],
563
            'json' => [
564
                'fileId' => $fileId,
565
            ],
566
        ]);
567
568
        return $response;
569
    }
570
571
    /**
572
     * uploads the file as "parts" of 100MB each.
573
     *
574
     * @param $filePath
575
     * @param $uploadUrl
576
     * @param $largeFileAuthToken
577
     *
578
     * @return array
579
     */
580
    protected function uploadParts($filePath, $uploadUrl, $largeFileAuthToken)
581
    {
582
        $return = [];
583
584
        $minimum_part_size = 100 * (1000 * 1000);
585
586
        $local_file_size = filesize($filePath);
587
        $total_bytes_sent = 0;
588
        $bytes_sent_for_part = $minimum_part_size;
589
        $sha1_of_parts = [];
590
        $part_no = 1;
591
        $file_handle = fopen($filePath, 'r');
592
593
        while ($total_bytes_sent < $local_file_size) {
594
595
            // Determine the number of bytes to send based on the minimum part size
596
            if (($local_file_size - $total_bytes_sent) < $minimum_part_size) {
597
                $bytes_sent_for_part = ($local_file_size - $total_bytes_sent);
598
            }
599
600
            // Get a sha1 of the part we are going to send
601
            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

601
            fseek(/** @scrutinizer ignore-type */ $file_handle, $total_bytes_sent);
Loading history...
602
            $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

602
            $data_part = fread(/** @scrutinizer ignore-type */ $file_handle, $bytes_sent_for_part);
Loading history...
603
            array_push($sha1_of_parts, sha1($data_part));
604
            fseek($file_handle, $total_bytes_sent);
605
606
            $response = $this->client->request('POST', $uploadUrl, [
607
                'headers' => [
608
                    'Authorization'                      => $largeFileAuthToken,
609
                    'Content-Length'                     => $bytes_sent_for_part,
610
                    'X-Bz-Part-Number'                   => $part_no,
611
                    'X-Bz-Content-Sha1'                  => $sha1_of_parts[$part_no - 1],
612
                ],
613
                'body' => $data_part,
614
            ]);
615
616
            $return[] = $response;
617
618
            // Prepare for the next iteration of the loop
619
            $part_no++;
620
            $total_bytes_sent = $bytes_sent_for_part + $total_bytes_sent;
621
        }
622
623
        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

623
        fclose(/** @scrutinizer ignore-type */ $file_handle);
Loading history...
624
625
        return $return;
626
    }
627
628
    /**
629
     * finishes the large file upload proceedure.
630
     *
631
     * @param $fileId
632
     * @param array $sha1s
633
     *
634
     * @return File
635
     */
636
    protected function finishLargeFile($fileId, array $sha1s)
637
    {
638
        $response = $this->client->request('POST', $this->apiUrl.'/b2_finish_large_file', [
639
            'headers' => [
640
                'Authorization' => $this->authToken,
641
            ],
642
            'json' => [
643
                'fileId'        => $fileId,
644
                'partSha1Array' => $sha1s,
645
            ],
646
        ]);
647
648
        return new File(
649
            $response['fileId'],
650
            $response['fileName'],
651
            $response['contentSha1'],
652
            $response['contentLength'],
653
            $response['contentType'],
654
            $response['fileInfo'],
655
            $response['bucketId'],
656
            $response['action'],
657
            $response['uploadTimestamp']
658
        );
659
    }
660
}
661