uploadx/uploadx
最新稳定版本:0.0.1
Composer 安装命令:
composer require uploadx/uploadx
包简介
A PHP library for uploading files with chunking, resume, multiple, and extensive validation.
README 文档
README
UploadX is a PHP file upload library that supports single and multiple uploads, chunked uploads with resume capability, multiple storage backends, and extensive validation.
Features
- 🚀 Single & Multiple Uploads - Upload one or many files at once
- 📦 Chunked Uploads - Split large files into manageable chunks
- 🔄 Resume Capability - Resume interrupted uploads from the last position
- 🗄️ Multiple Storage Backends - Local filesystem, Amazon S3, MinIO, and more
- ✅ Validation - MIME type, file size, checksum, and custom validation
- 🔒 Security - Path traversal protection, secure filenames, checksum verification
- 📊 Progress Tracking - Real-time upload progress and metrics
- 🔔 Event System - PSR-14 event dispatcher integration
- 📝 Logging - PSR-3 compatible logging with Monolog
- ⚡ Performance - Memory-efficient chunk processing, streaming support
- 🧪 Fully Tested - Comprehensive unit, feature, and integration tests
Requirements
- PHP 8.3 or higher
- Composer
- Required extensions:
json,fileinfo,hash,mbstring
Installation
composer require uploadx/uploadx
Quick Start
Basic Upload
<?php require_once __DIR__ . '/vendor/autoload.php'; use UploadX\UploadX; use UploadX\Exceptions\UploadException; $uploadx = new UploadX([ 'storage_path' => __DIR__ . '/uploads', 'storage_url' => '/uploads', ]); try { $result = $uploadx->upload( __DIR__ . '/test-files/sample.txt', 'documents/sample.txt' ); echo "Upload successful!\n"; echo "Path: {$result->path()}\n"; echo "Filename: {$result->filename()}\n"; echo "Size: {$result->size()} bytes\n"; echo "MIME Type: {$result->mimeType()}\n"; echo "URL: {$result->url()}\n"; echo "Checksum: {$result->checksum()}\n"; echo "Duration: {$result->duration()} seconds\n"; } catch (UploadException $e) { echo "Upload failed: {$e->getMessage()}\n"; }
Upload with Validation
<?php require_once __DIR__ . '/vendor/autoload.php'; use UploadX\UploadX; use UploadX\Exceptions\UploadException; use UploadX\Exceptions\ChecksumException; $uploadx = new UploadX([ 'storage_path' => __DIR__ . '/uploads', 'allowed_mimes' => ['image/jpeg', 'image/png', 'application/pdf'], 'max_file_size' => 10 * 1024 * 1024, // 10MB ]); try { $result = $uploadx->upload( __DIR__ . '/test-files/photo.jpeg', 'images/photo.jpeg', [ 'allowed_mimes' => ['image/jpeg', 'image/png'], 'max_size' => 5 * 1024 * 1024, // 5MB 'checksum' => hash_file('sha256', __DIR__ . '/test-files/photo.jpeg'), 'secure_filename' => true, 'metadata' => [ 'user_id' => 123, 'category' => 'profile_photos', 'uploaded_at' => date('Y-m-d H:i:s'), ], ] ); echo "Upload with validation successful!\n"; echo "Metadata: " . json_encode($result->metadata(), JSON_PRETTY_PRINT) . "\n"; } catch (ChecksumException $e) { echo "Checksum mismatch!\n"; echo "Expected: {$e->expected()}\n"; echo "Actual: {$e->actual()}\n"; } catch (UploadException $e) { echo "Upload failed: {$e->getMessage()}\n"; }
Multiple Upload
<?php require_once __DIR__ . '/vendor/autoload.php'; use UploadX\UploadX; use UploadX\Exceptions\UploadException; $uploadx = new UploadX([ 'storage_path' => __DIR__ . '/uploads', 'storage_url' => '/uploads', ]); $files = [ [ 'source' => __DIR__ . '/test-files/file1.txt', 'destination' => 'documents/file1.txt', 'options' => [ 'metadata' => ['type' => 'text', 'priority' => 1], ], ], [ 'source' => __DIR__ . '/test-files/file2.pdf', 'destination' => 'documents/file2.pdf', 'options' => [ 'metadata' => ['type' => 'pdf', 'priority' => 2], ], ], [ 'source' => __DIR__ . '/test-files/file3.jpg', 'destination' => 'images/file3.jpg', 'options' => [ 'allowed_mimes' => ['image/jpeg', 'image/png'], 'max_size' => 10 * 1024 * 1024, 'metadata' => ['type' => 'image', 'priority' => 3], ], ], ]; try { $results = $uploadx->uploadMultiple($files); echo "Successful uploads: " . count($results['successful']) . "\n"; foreach ($results['successful'] as $index => $result) { echo "[{$index}] {$result->filename()} ({$result->size()} bytes)\n"; echo " URL: {$result->url()}\n"; } echo "\nFailed uploads: " . count($results['failed']) . "\n"; foreach ($results['failed'] as $failure) { echo "[{$failure['index']}] {$failure['file']}: {$failure['error']}\n"; } } catch (UploadException $e) { echo "Error: {$e->getMessage()}\n"; }
Batch Upload from Directory
<?php require_once __DIR__ . '/vendor/autoload.php'; use UploadX\UploadX; use UploadX\Exceptions\UploadException; function uploadFromDirectory(UploadX $uploadx, string $directory, string $prefix = ''): array { $files = []; $allowedExtensions = ['txt', 'pdf', 'jpg', 'png', 'zip']; $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS) ); foreach ($iterator as $file) { if ($file->isFile()) { $extension = strtolower($file->getExtension()); if (in_array($extension, $allowedExtensions)) { $relativePath = str_replace($directory, '', $file->getPathname()); $relativePath = ltrim($relativePath, '/'); $files[] = [ 'source' => $file->getPathname(), 'destination' => ($prefix ? $prefix . '/' : '') . $relativePath, 'options' => [ 'secure_filename' => false, ], ]; } } } return $uploadx->uploadMultiple($files); } $uploadx = new UploadX([ 'storage_path' => __DIR__ . '/uploads', ]); try { $results = uploadFromDirectory($uploadx, __DIR__ . '/test-files', 'batch-uploads'); echo "Batch upload complete!\n"; echo "Successful: " . count($results['successful']) . "\n"; echo "Failed: " . count($results['failed']) . "\n"; foreach ($results['successful'] as $result) { echo "- {$result->filename()}\n"; } } catch (UploadException $e) { echo "Error: {$e->getMessage()}\n"; }
Chunked Upload
<?php require_once __DIR__ . '/vendor/autoload.php'; use UploadX\UploadX; use UploadX\Support\HashHelper; use UploadX\Exceptions\UploadException; use UploadX\Exceptions\InvalidChunkException; $uploadx = new UploadX([ 'storage_path' => __DIR__ . '/uploads', 'chunk_size' => 1 * 1024 * 1024, // 1MB chunks 'temp_directory' => __DIR__ . '/temp/chunks', 'sessions_directory' => __DIR__ . '/temp/sessions', ]); $testFilePath = __DIR__ . '/test-files/large-file.dat'; $chunkSize = 1 * 1024 * 1024; // Initialize chunk upload $init = $uploadx->initChunkUpload( 'large-file.dat', filesize($testFilePath), (int) ceil(filesize($testFilePath) / $chunkSize), ['description' => 'Chunk upload demo file'] ); $sessionId = $init['session_id']; // Upload chunks $handle = fopen($testFilePath, 'rb'); $totalChunks = (int) ceil(filesize($testFilePath) / $chunkSize); for ($i = 1; $i <= $totalChunks; $i++) { $chunkData = fread($handle, $chunkSize); if ($chunkData === false) break; try { $result = $uploadx->uploadChunk([ 'session_id' => $sessionId, 'chunk_number' => $i, 'total_chunks' => $totalChunks, 'chunk_size' => strlen($chunkData), 'total_size' => filesize($testFilePath), 'data' => $chunkData, 'checksum' => HashHelper::chunkChecksum($chunkData), 'filename' => 'large-file.dat', 'mime_type' => 'application/octet-stream', ]); echo "Chunk {$i}/{$totalChunks} OK\n"; } catch (UploadException | InvalidChunkException $e) { echo "Chunk {$i} FAILED: {$e->getMessage()}\n"; } } fclose($handle); // Final result if ($result->isSuccessful()) { echo "Upload complete!\n"; echo "URL: {$result->url()}\n"; echo "Path: {$result->path()}\n"; }
Resume Upload
<?php require_once __DIR__ . '/vendor/autoload.php'; use UploadX\UploadX; use UploadX\Support\HashHelper; use UploadX\Exceptions\UploadException; use UploadX\Exceptions\InvalidChunkException; $uploadx = new UploadX([ 'storage_path' => __DIR__ . '/uploads', 'chunk_size' => 1 * 1024 * 1024, // 1MB chunks 'temp_directory' => __DIR__ . '/temp/chunks', 'sessions_directory' => __DIR__ . '/temp/sessions', ]); // Initialize $init = $uploadx->initChunkUpload('resume-file.dat', $fileSize, $totalChunks); $sessionId = $init['session_id']; // ... upload some chunks ... // Resume interrupted upload $sessionInfo = $uploadx->resume($sessionId); echo "Session found:\n"; echo "Filename: {$sessionInfo['filename']}\n"; echo "Total chunks: {$sessionInfo['total_chunks']}\n"; echo "Uploaded chunks: " . implode(', ', $sessionInfo['uploaded_chunks']) . "\n"; echo "Missing chunks: " . implode(', ', $sessionInfo['missing_chunks']) . "\n"; echo "Progress: " . round($sessionInfo['progress'], 1) . "%\n"; // Upload remaining missing chunks $handle = fopen($testFilePath, 'rb'); foreach ($sessionInfo['missing_chunks'] as $chunkNum) { $offset = ($chunkNum - 1) * $chunkSize; fseek($handle, $offset); $chunkData = fread($handle, $chunkSize); $uploadx->uploadChunk([ 'session_id' => $sessionId, 'chunk_number' => $chunkNum, 'total_chunks' => $totalChunks, 'chunk_size' => strlen($chunkData), 'total_size' => $fileSize, 'data' => $chunkData, 'checksum' => HashHelper::chunkChecksum($chunkData), 'filename' => 'resume-file.dat', 'mime_type' => 'application/octet-stream', ]); } fclose($handle);
S3 Storage
<?php require_once __DIR__ . '/vendor/autoload.php'; use UploadX\UploadX; use UploadX\Storage\S3Storage; use UploadX\Exceptions\StorageException; $s3Storage = new S3Storage( config: [ 'version' => 'latest', 'region' => getenv('AWS_REGION') ?: 'us-east-1', 'credentials' => [ 'key' => getenv('AWS_KEY') ?: 'YOUR_AWS_KEY', 'secret' => getenv('AWS_SECRET') ?: 'YOUR_AWS_SECRET', ], ], bucket: getenv('S3_BUCKET') ?: 'my-bucket', prefix: 'uploads/example' ); // Upload file try { $result = $s3Storage->store( __DIR__ . '/test-files/sample.txt', 'documents/sample.txt' ); echo "Uploaded to: {$result}\n"; echo "URL: {$s3Storage->url('documents/sample.txt')}\n"; // Upload with metadata and encryption $result2 = $s3Storage->store( __DIR__ . '/test-files/photo.jpg', 'images/photo.jpg', [ 'acl' => 'public-read', 'storage_class' => 'STANDARD', 'metadata' => [ 'author' => 'UploadX', 'category' => 'photos', ], 'server_side_encryption' => 'AES256', ] ); // Other operations $exists = $s3Storage->exists('documents/sample.txt'); $size = $s3Storage->size('documents/sample.txt'); $mime = $s3Storage->mimeType('documents/sample.txt'); $presignedUrl = $s3Storage->temporaryUrl('documents/sample.txt', 3600); $content = $s3Storage->retrieve('documents/sample.txt'); $s3Storage->delete('documents/sample.txt'); } catch (StorageException $e) { echo "Storage error: {$e->getMessage()}\n"; } // UploadX with S3 Storage $uploadx = new UploadX(storage: $s3Storage); $result = $uploadx->upload( __DIR__ . '/test-files/sample.txt', 'general/sample.txt' ); echo "Upload via UploadX successful!\n"; echo "Path: {$result->path()}\n"; echo "Storage Driver: {$result->storageDriver()}\n";
MinIO Storage
<?php require_once __DIR__ . '/vendor/autoload.php'; use UploadX\UploadX; use UploadX\Storage\MinioStorage; use UploadX\Exceptions\StorageException; $minioStorage = new MinioStorage( endpoint: getenv('MINIO_ENDPOINT') ?: 'http://localhost:9000', accessKey: getenv('MINIO_ACCESS_KEY') ?: 'minioadmin', secretKey: getenv('MINIO_SECRET_KEY') ?: 'minioadmin', bucket: getenv('MINIO_BUCKET') ?: 'uploads', region: 'us-east-1', prefix: 'uploads/example', usePathStyleEndpoint: true ); // Ensure bucket exists $minioStorage->ensureBucketExists(); // Basic operations try { $storedPath = $minioStorage->store( __DIR__ . '/test-files/sample.txt', 'documents/sample.txt' ); echo "Stored at: {$storedPath}\n"; echo "URL: {$minioStorage->url('documents/sample.txt')}\n"; // Store with metadata $minioStorage->store( __DIR__ . '/test-files/photo.jpg', 'images/photo.jpg', [ 'acl' => 'public-read', 'metadata' => [ 'author' => 'UploadX', 'description' => 'Example photo for MinIO', ], ] ); // Check existence, info, temporary URL, retrieve, delete $exists = $minioStorage->exists('documents/sample.txt'); $size = $minioStorage->size('documents/sample.txt'); $mime = $minioStorage->mimeType('documents/sample.txt'); $tempUrl = $minioStorage->temporaryUrl('documents/sample.txt', 3600); $content = $minioStorage->retrieve('documents/sample.txt'); $minioStorage->delete('documents/sample.txt'); } catch (StorageException $e) { echo "Storage error: {$e->getMessage()}\n"; } // UploadX with MinIO $uploadx = new UploadX(storage: $minioStorage); $result = $uploadx->upload( __DIR__ . '/test-files/sample.txt', 'general/sample.txt' );
Web Upload Handler
<?php require_once __DIR__ . '/vendor/autoload.php'; use UploadX\UploadX; use UploadX\Support\HashHelper; use UploadX\Exceptions\UploadException; use UploadX\Exceptions\InvalidChunkException; class WebUploadHandler { private UploadX $uploadx; private array $config; public function __construct(array $config = []) { $this->config = array_merge([ 'upload_dir' => __DIR__ . '/uploads/web', 'max_file_size' => 100 * 1024 * 1024, // 100MB 'allowed_types' => [ 'image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/zip', 'text/plain', ], 'chunk_size' => 5 * 1024 * 1024, // 5MB ], $config); $this->uploadx = new UploadX([ 'storage_path' => $this->config['upload_dir'], 'storage_url' => '/uploads/web', 'max_file_size' => $this->config['max_file_size'], 'allowed_mimes' => $this->config['allowed_types'], 'chunk_size' => $this->config['chunk_size'], 'temp_directory' => __DIR__ . '/temp/web-chunks', 'sessions_directory' => __DIR__ . '/temp/web-sessions', ]); } /** * Handle single file upload from form */ public function handleSingleUpload(array $fileField, string $destination = ''): array { if (!isset($fileField['tmp_name']) || empty($fileField['tmp_name'])) { return [ 'success' => false, 'error' => 'No file uploaded', 'code' => 'NO_FILE', ]; } if ($fileField['error'] !== UPLOAD_ERR_OK) { $errorMessages = [ UPLOAD_ERR_INI_SIZE => 'File exceeds server upload limit', UPLOAD_ERR_FORM_SIZE => 'File exceeds form upload limit', UPLOAD_ERR_PARTIAL => 'File was only partially uploaded', UPLOAD_ERR_NO_FILE => 'No file was uploaded', UPLOAD_ERR_NO_TMP_DIR => 'Server missing temporary directory', UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk', ]; return [ 'success' => false, 'error' => $errorMessages[$fileField['error']] ?? 'Unknown upload error', 'code' => 'UPLOAD_ERROR', ]; } $filename = $fileField['name']; $source = $fileField['tmp_name']; $destination = $destination ?: 'uploads/' . date('Y/m/d/') . $filename; try { $result = $this->uploadx->upload($source, $destination, [ 'metadata' => [ 'original_name' => $filename, 'client_ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown', 'upload_time' => date('Y-m-d H:i:s'), ], ]); return [ 'success' => true, 'file' => [ 'id' => $result->sessionId(), 'name' => $result->filename(), 'size' => $result->size(), 'type' => $result->mimeType(), 'url' => $result->url(), 'checksum' => $result->checksum(), ], ]; } catch (UploadException $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => 'UPLOAD_FAILED', ]; } } /** * Handle chunk upload from web (JavaScript File API) */ public function handleChunkUpload(array $input): array { $action = $input['action'] ?? 'upload'; switch ($action) { case 'init': return $this->initChunkUpload($input); case 'upload': return $this->uploadChunk($input); case 'resume': return $this->resumeUpload($input); case 'cancel': return $this->cancelUpload($input); default: return [ 'success' => false, 'error' => 'Unknown action', 'code' => 'UNKNOWN_ACTION', ]; } } private function initChunkUpload(array $input): array { $filename = $input['filename'] ?? ''; $fileSize = (int)($input['fileSize'] ?? 0); $totalChunks = (int)($input['totalChunks'] ?? 0); if (empty($filename) || $fileSize <= 0 || $totalChunks <= 0) { return [ 'success' => false, 'error' => 'Invalid upload parameters', 'code' => 'INVALID_PARAMS', ]; } try { $init = $this->uploadx->initChunkUpload( $filename, $fileSize, $totalChunks, ['client_ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', 'start_time' => date('Y-m-d H:i:s')] ); return [ 'success' => true, 'session_id' => $init['session_id'], 'chunk_size' => $this->config['chunk_size'], ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage(), 'code' => 'INIT_FAILED', ]; } } private function uploadChunk(array $input): array { $sessionId = $input['session_id'] ?? ''; $chunkNumber = (int)($input['chunk_number'] ?? 0); $totalChunks = (int)($input['total_chunks'] ?? 0); $totalSize = (int)($input['total_size'] ?? 0); $filename = $input['filename'] ?? ''; $chunkData = $input['data'] ?? ''; if (empty($chunkData) && isset($_FILES['chunk'])) { $chunkData = file_get_contents($_FILES['chunk']['tmp_name']); } if (isset($input['data_base64'])) { $chunkData = base64_decode($input['data_base64']); } if (empty($chunkData)) { return ['success' => false, 'error' => 'No chunk data received', 'code' => 'NO_DATA']; } try { $result = $this->uploadx->uploadChunk([ 'session_id' => $sessionId, 'chunk_number' => $chunkNumber, 'total_chunks' => $totalChunks, 'chunk_size' => strlen($chunkData), 'total_size' => $totalSize, 'data' => $chunkData, 'checksum' => HashHelper::chunkChecksum($chunkData), 'filename' => $filename, 'mime_type' => $input['mime_type'] ?? 'application/octet-stream', ]); return [ 'success' => true, 'chunk_number' => $chunkNumber, 'session_id' => $sessionId, 'progress' => ($chunkNumber / $totalChunks) * 100, 'file' => $result->isSuccessful() ? [ 'url' => $result->url(), 'filename' => $result->filename(), 'size' => $result->size(), ] : null, ]; } catch (InvalidChunkException $e) { return ['success' => false, 'error' => "Chunk {$chunkNumber} validation failed: {$e->getMessage()}", 'code' => 'CHUNK_INVALID']; } catch (UploadException $e) { return ['success' => false, 'error' => $e->getMessage(), 'code' => 'CHUNK_FAILED']; } } private function resumeUpload(array $input): array { try { $session = $this->uploadx->resume($input['session_id'] ?? ''); return [ 'success' => true, 'session' => $session, 'missing_chunks' => $session['missing_chunks'], 'progress' => $session['progress'], ]; } catch (UploadException $e) { return ['success' => false, 'error' => $e->getMessage(), 'code' => 'RESUME_FAILED']; } } private function cancelUpload(array $input): array { try { $this->uploadx->cancel($input['session_id'] ?? ''); return ['success' => true, 'message' => 'Upload cancelled successfully']; } catch (\Exception $e) { return ['success' => false, 'error' => $e->getMessage(), 'code' => 'CANCEL_FAILED']; } } public function getMetrics(): array { return $this->uploadx->getMetrics(); } public function cleanup(int $maxAge = 86400): int { return $this->uploadx->cleanup($maxAge); } } // Example usage $handler = new WebUploadHandler([ 'upload_dir' => __DIR__ . '/uploads/web', 'max_file_size' => 50 * 1024 * 1024, 'allowed_types' => ['image/jpeg', 'image/png', 'application/pdf'], ]); // Single upload (simulated from $_FILES) $result = $handler->handleSingleUpload([ 'name' => 'test-image.jpg', 'type' => 'image/jpeg', 'tmp_name' => __DIR__ . '/test-files/photo.jpeg', 'error' => UPLOAD_ERR_OK, 'size' => filesize(__DIR__ . '/test-files/photo.jpeg'), ], 'images/test-image.jpg'); // Chunk upload init (simulated from JavaScript) $initResult = $handler->handleChunkUpload([ 'action' => 'init', 'filename' => 'large-video.mp4', 'fileSize' => 10485760, // 10MB 'totalChunks' => 2, ]);
Configuration
| Option | Default | Description |
|---|---|---|
storage_driver |
local |
Storage driver (local, s3, minio) |
storage_path |
./uploads |
Base path for local storage |
storage_url |
/uploads |
Base URL for file access |
chunk_size |
5242880 |
Default chunk size (5MB) |
max_file_size |
104857600 |
Maximum file size (100MB) |
allowed_mimes |
[] |
Allowed MIME types (empty = all) |
temp_directory |
sys_get_temp_dir() . '/uploadx' |
Temporary directory for chunks |
sessions_directory |
sys_get_temp_dir() . '/uploadx/sessions' |
Directory for session data |
secret_key |
'' |
Secret key for signed uploads |
log_file |
'' |
Path to log file |
log_level |
Warning |
Logging level |
Storage Drivers
Local Storage
Default storage driver. Stores files on the local filesystem.
use UploadX\Storage\LocalStorage; $storage = new LocalStorage( basePath: '/path/to/uploads', baseUrl: '/uploads', permissions: 0755 );
Supported operations:
store($sourcePath, $destinationPath, $options)- Store a fileretrieve($path)- Retrieve a filedelete($path)- Delete a fileexists($path)- Check file existenceurl($path)- Get file URLsize($path)- Get file sizemimeType($path)- Get MIME typetemporaryUrl($path, $expiresIn)- Generate temporary URLname()- Driver namebasePath()- Base path
Amazon S3
Requires AWS SDK. Stores files on Amazon S3.
use UploadX\Storage\S3Storage; $storage = new S3Storage( config: [ 'version' => 'latest', 'region' => getenv('AWS_REGION') ?: 'us-east-1', 'credentials' => [ 'key' => getenv('AWS_KEY') ?: 'YOUR_AWS_KEY', 'secret' => getenv('AWS_SECRET') ?: 'YOUR_AWS_SECRET', ], ], bucket: getenv('S3_BUCKET') ?: 'my-bucket', prefix: 'uploads' );
Additional options when storing:
storage_class- Storage class (STANDARD, REDUCED_REDUNDANCY, etc.)acl- Access control (private, public-read, etc.)metadata- Custom metadataserver_side_encryption- Server-side encryption (AES256)
MinIO
Compatible with S3 API. Stores files on MinIO server.
use UploadX\Storage\MinioStorage; $storage = new MinioStorage( endpoint: getenv('MINIO_ENDPOINT') ?: 'http://localhost:9000', accessKey: getenv('MINIO_ACCESS_KEY') ?: 'minioadmin', secretKey: getenv('MINIO_SECRET_KEY') ?: 'minioadmin', bucket: getenv('MINIO_BUCKET') ?: 'uploads', region: 'us-east-1', prefix: 'uploads', usePathStyleEndpoint: true );
MinIO-specific methods:
ensureBucketExists()- Create bucket if it doesn't exist
Validation
MIME Type Validation
$result = $uploadx->upload($source, $destination, [ 'allowed_mimes' => ['image/jpeg', 'image/png', 'application/pdf'], ]);
Size Validation
$result = $uploadx->upload($source, $destination, [ 'max_size' => 10485760 // 10MB ]);
Checksum Validation
$expectedChecksum = hash_file('sha256', $sourceFile); $result = $uploadx->upload($source, $destination, [ 'checksum' => $expectedChecksum, ]);
Multiple Validations
$result = $uploadx->upload($source, $destination, [ 'allowed_mimes' => ['image/jpeg', 'image/png'], 'max_size' => 5 * 1024 * 1024, // 5MB 'checksum' => hash_file('sha256', $source), 'secure_filename' => true, 'metadata' => [ 'user_id' => 123, 'category' => 'photos', ], ]);
Events
UploadX dispatches PSR-14 events during the upload process:
UploadStarted- When upload beginsChunkUploaded- When a chunk is receivedUploadCompleted- When upload completesUploadFailed- When upload fails
use Symfony\Component\EventDispatcher\EventDispatcher; use UploadX\Events\UploadCompleted; $dispatcher = new EventDispatcher(); $dispatcher->addListener(UploadCompleted::class, function (UploadCompleted $event) { echo "File {$event->filename()} has been uploaded successfully!\n"; echo "Size: {$event->fileSize()} bytes\n"; echo "Duration: {$event->durationSeconds()} seconds\n"; }); $uploadx = new UploadX(eventDispatcher: $dispatcher);
Error Handling
UploadX throws specific exceptions for different error types:
UploadException- General upload error (codes: 1001-1005)InvalidChunkException- Chunk validation errorStorageException- Storage backend errorChecksumException- Checksum validation error
try { $result = $uploadx->upload($source, $destination); } catch (UploadException $e) { echo "Upload failed: " . $e->getMessage(); echo "Code: " . $e->getCode(); } catch (StorageException $e) { echo "Storage error: " . $e->getMessage(); } catch (ChecksumException $e) { echo "Checksum mismatch!\n"; echo "Expected: " . $e->expected() . "\n"; echo "Actual: " . $e->actual(); }
API Reference
UploadX (Main Class)
| Method | Description |
|---|---|
upload($source, $destination, $options) |
Upload a single file |
uploadMultiple($files) |
Upload multiple files |
initChunkUpload($filename, $fileSize, $totalChunks, $metadata) |
Initialize chunk upload |
uploadChunk($chunkData) |
Upload a single chunk |
resume($sessionId) |
Resume interrupted upload |
cancel($sessionId) |
Cancel upload |
progress() |
Get upload progress |
getMetrics() |
Get upload metrics |
cleanup($maxAge) |
Clean up old sessions |
storage() |
Get storage instance |
manager() |
Get upload manager |
UploadResult
| Method | Description |
|---|---|
path() |
Storage path |
filename() |
Original filename |
size() |
File size in bytes |
mimeType() |
MIME type |
url() |
Public URL |
storageDriver() |
Storage driver name |
sessionId() |
Upload session ID |
checksum() |
SHA-256 checksum |
duration() |
Upload duration in seconds |
metadata() |
Upload metadata |
isSuccessful() |
Whether the upload succeeded |
toArray() |
Convert to array |
UploadSession
| Method | Description |
|---|---|
sessionId() |
Session identifier |
filename() |
Filename |
totalSize() |
Total file size |
totalChunks() |
Total chunks |
uploadedChunks() |
Uploaded chunks |
missingChunks() |
Missing chunks |
progress() |
Progress percentage |
duration() |
Session duration |
averageSpeed() |
Average speed |
isCompleted() |
Whether session is complete |
isExpired($timeout) |
Whether session is expired |
toArray() |
Convert to array |
Chunk Interface
| Method | Description |
|---|---|
chunkId() |
Chunk identifier |
sessionId() |
Session identifier |
chunkNumber() |
Chunk number (1-based) |
totalChunks() |
Total chunks |
chunkSize() |
Chunk size |
totalSize() |
Total file size |
data() |
Chunk data |
checksum() |
Chunk checksum |
originalFilename() |
Original filename |
mimeType() |
MIME type |
offset() |
Offset in file |
validate() |
Validate checksum |
Storage Interface
| Method | Description |
|---|---|
store($source, $destination, $options) |
Store a file |
retrieve($path) |
Retrieve a file |
delete($path) |
Delete a file |
exists($path) |
Check existence |
url($path) |
Get URL |
size($path) |
Get size |
mimeType($path) |
Get MIME type |
temporaryUrl($path, $expiresIn) |
Generate temporary URL |
name() |
Driver name |
basePath() |
Base path |
License
MIT License. See LICENSE file for more information.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 2
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: Unknown
- 更新时间: 2026-06-12