PHP code example of nks-hub / nette-cloudflare-r2

1. Go to this page and download the library: Download nks-hub/nette-cloudflare-r2 library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

nks-hub / nette-cloudflare-r2 example snippets


use NksHub\NetteCloudflareR2\Client\R2Client;

class MyPresenter extends Nette\Application\UI\Presenter
{
    public function __construct(
        private R2Client $r2
    ) {}

    public function actionUpload(): void
    {
        // Upload string content
        $url = $this->r2->upload('path/to/file.txt', 'Hello World!');

        // Upload from local file
        $url = $this->r2->uploadFromPath('images/photo.jpg', '/local/path/photo.jpg');

        // Download content
        $content = $this->r2->get('path/to/file.txt');

        // Download to local file
        $this->r2->download('images/photo.jpg', '/local/destination.jpg');

        // Delete
        $this->r2->delete('path/to/file.txt');

        // Check existence
        if ($this->r2->exists('path/to/file.txt')) {
            // File exists
        }

        // Get metadata
        $metadata = $this->r2->getMetadata('path/to/file.txt');
        echo $metadata->getSize();
        echo $metadata->getContentType();
        echo $metadata->getFormattedSize(); // "1.5 MB"
    }
}

use Nette\Http\FileUpload;
use NksHub\NetteCloudflareR2\Client\R2Client;

class GalleryPresenter extends Nette\Application\UI\Presenter
{
    public function __construct(
        private R2Client $r2
    ) {}

    public function handleUploadPhoto(): void
    {
        /** @var FileUpload $file */
        $file = $this->getHttpRequest()->getFile('photo');

        if ($file && $file->isOk() && $file->isImage()) {
            // Upload with auto-generated filename
            $url = $this->r2->uploadFile($file, 'gallery/' . $this->user->id);

            // Save URL to database
            $this->galleryRepository->insert([
                'user_id' => $this->user->id,
                'url' => $url,
            ]);

            $this->flashMessage('Photo uploaded successfully');
        }

        $this->redirect('this');
    }
}

use NksHub\NetteCloudflareR2\Storage\UploadOptions;
use NksHub\NetteCloudflareR2\Storage\StorageClass;

// Create options
$options = UploadOptions::create()
    ->withContentType('image/jpeg')
    ->withCacheControl('max-age=86400')
    ->withStorageClass(StorageClass::INFREQUENT_ACCESS)
    ->withMetadata(['author' => 'John Doe']);

$url = $r2->upload('photo.jpg', $content, $options);

// Temporary download URL (1 hour)
$presignedUrl = $r2->getPresignedUrl('private/document.pdf', 3600);
echo $presignedUrl->getUrl();
echo $presignedUrl->getExpiresAt()->format('Y-m-d H:i:s');

// Temporary upload URL
$uploadUrl = $r2->getPresignedUploadUrl('uploads/new-file.jpg', 600);

// Check if expired
if ($presignedUrl->isExpired()) {
    // Generate new URL
}

// List with pagination
$result = $r2->list('images/', maxKeys: 100);
foreach ($result['objects'] as $object) {
    echo $object->getKey();
    echo $object->getSize();
}

if ($result['isTruncated']) {
    // Get next page
    $nextResult = $r2->list('images/', 100, $result['nextToken']);
}

// List all (auto-pagination)
foreach ($r2->listAll('images/') as $object) {
    echo $object->getKey();
}

// Count objects
$count = $r2->count('images/');

use NksHub\NetteCloudflareR2\Client\R2ClientFactory;

class MyService
{
    public function __construct(
        private R2Client $r2,           // Default bucket
        private R2ClientFactory $factory
    ) {}

    public function uploadToImages(string $content): string
    {
        return $this->factory->create('images')->upload('file.jpg', $content);
    }

    public function uploadToBackups(string $content): string
    {
        return $this->factory->create('backups')->upload('backup.zip', $content);
    }
}

// Copy object
$newUrl = $r2->copy('original.jpg', 'copy.jpg');

// Update metadata
$r2->setMetadata('file.jpg', ['version' => '2']);

// Change storage class
$r2->changeStorageClass('archive.zip', StorageClass::INFREQUENT_ACCESS);

// Automatic multipart for large files (>100MB by default)
$url = $r2->upload('large-file.zip', $largeContent);

// Manual multipart upload
$multipart = $r2->multipart();
$uploadId = $multipart->create('huge-file.zip');

$parts = [];
$partNumber = 1;
foreach ($chunks as $chunk) {
    $parts[] = $multipart->uploadPart('huge-file.zip', $uploadId, $partNumber++, $chunk);
}

$multipart->complete('huge-file.zip', $uploadId, $parts);

// With progress tracking
use NksHub\NetteCloudflareR2\Upload\ChunkedUploader;

$uploader = new ChunkedUploader($r2);
$uploader->onProgress(function (int $uploaded, int $total) {
    echo round($uploaded / $total * 100) . '%';
});
$url = $uploader->upload('huge-file.zip', '/local/path/huge-file.zip');

// Add expiration rule (delete after 90 days)
$r2->lifecycle()->addExpirationRule('logs/', days: 90);

// Transition to Infrequent Access after 30 days
$r2->lifecycle()->addTransitionRule(
    'archive/',
    days: 30,
    targetClass: StorageClass::INFREQUENT_ACCESS
);

// Abort incomplete multipart uploads after 7 days
$r2->lifecycle()->addAbortIncompleteMultipartRule(days: 7);

// Get current rules
$rules = $r2->lifecycle()->get();

// Remove a rule
$r2->lifecycle()->removeRule('expire-logs-90d');

// List buckets
$buckets = $r2->buckets()->list();

// Create bucket
$r2->buckets()->create('new-bucket', locationHint: 'weur');

// Delete bucket
$r2->buckets()->delete('old-bucket');

// CORS configuration
$r2->buckets()->setCors([
    [
        'AllowedOrigins' => ['https://example.com'],
        'AllowedMethods' => ['GET', 'PUT', 'POST'],
        'AllowedHeaders' => ['*'],
        'MaxAgeSeconds' => 3600,
    ],
]);

use NksHub\NetteCloudflareR2\Api\CloudflareApi;

$api = new CloudflareApi($accountId, $apiToken);

// Custom domains
$api->attachCustomDomain('my-bucket', 'cdn.example.com');
$api->listCustomDomains('my-bucket');

// Enable r2.dev public access
$api->setManagedDomain('my-bucket', enabled: true);

// Event notifications
$api->createEventNotification(
    'my-bucket',
    'queue-id',
    ['object-create', 'object-delete'],
    prefix: 'uploads/'
);

// Get metrics
$metrics = $api->getMetrics();

// Temporary credentials
$creds = $api->createTempCredentials(
    'my-bucket',
    permission: 'object-read-only',
    ttlSeconds: 3600
);

use NksHub\NetteCloudflareR2\Upload\StreamUploader;
use NksHub\NetteCloudflareR2\Download\StreamDownloader;

// Upload from URL
$streamUploader = new StreamUploader($r2);
$url = $streamUploader->uploadFromUrl('image.jpg', 'https://example.com/image.jpg');

// Upload base64
$url = $streamUploader->uploadBase64('image.png', $base64Data);

// Stream download
$streamDownloader = new StreamDownloader($r2);
$streamDownloader->streamToOutput('file.pdf', $this->getHttpResponse(), 'document.pdf');

// Create FileResponse
$response = $streamDownloader->createFileResponse('file.pdf', 'download.pdf');
$this->sendResponse($response);

use NksHub\NetteCloudflareR2\Storage\StorageClass;

$options = UploadOptions::create()
    ->withStorageClass(StorageClass::INFREQUENT_ACCESS);

use NksHub\NetteCloudflareR2\Exception\R2Exception;
use NksHub\NetteCloudflareR2\Exception\ObjectException;
use NksHub\NetteCloudflareR2\Exception\AuthenticationException;
use NksHub\NetteCloudflareR2\Exception\RateLimitException;

try {
    $r2->get('non-existent-file.txt');
} catch (ObjectException $e) {
    // Object not found
    echo $e->getR2ErrorCode(); // 10007
} catch (AuthenticationException $e) {
    // Invalid credentials
} catch (RateLimitException $e) {
    // Rate limited (1 write/second per key)
    sleep($e->getRetryAfter());
} catch (R2Exception $e) {
    // Other R2 errors
}