friendship ended with social-app. php is my new best friend
1--- 2title: File Uploads 3description: Learn how to upload files and work with multipart form data 4--- 5 6# File Uploads 7 8This guide explains how to upload files and work with multipart form data using the Fetch HTTP package. 9 10## Basic File Upload 11 12To upload a file, you need to use multipart form data. The simplest way is with the helper functions: 13 14```php 15// Upload a file 16$response = fetch('https://api.example.com/upload', [ 17 'method' => 'POST', 18 'multipart' => [ 19 [ 20 'name' => 'file', 21 'contents' => file_get_contents('/path/to/image.jpg'), 22 'filename' => 'upload.jpg', 23 'headers' => ['Content-Type' => 'image/jpeg'] 24 ], 25 [ 26 'name' => 'description', 27 'contents' => 'Profile picture upload' 28 ] 29 ] 30]); 31 32// Check if upload was successful 33if ($response->successful()) { 34 $result = $response->json(); 35 echo "File uploaded successfully. URL: " . $result['url']; 36} else { 37 echo "Upload failed with status: " . $response->status(); 38} 39``` 40 41## Using ClientHandler for File Uploads 42 43You can also use the ClientHandler class for more control: 44 45```php 46use Fetch\Http\ClientHandler; 47 48$client = ClientHandler::create(); 49$response = $client->withMultipart([ 50 [ 51 'name' => 'file', 52 'contents' => file_get_contents('/path/to/document.pdf'), 53 'filename' => 'document.pdf', 54 'headers' => ['Content-Type' => 'application/pdf'] 55 ], 56 [ 57 'name' => 'document_type', 58 'contents' => 'invoice' 59 ], 60 [ 61 'name' => 'description', 62 'contents' => 'Monthly invoice #12345' 63 ] 64])->post('https://api.example.com/documents'); 65``` 66 67## Uploading Multiple Files 68 69You can upload multiple files in a single request: 70 71```php 72$response = fetch('https://api.example.com/gallery', [ 73 'method' => 'POST', 74 'multipart' => [ 75 [ 76 'name' => 'files[]', 77 'contents' => file_get_contents('/path/to/image1.jpg'), 78 'filename' => 'image1.jpg', 79 'headers' => ['Content-Type' => 'image/jpeg'] 80 ], 81 [ 82 'name' => 'files[]', 83 'contents' => file_get_contents('/path/to/image2.jpg'), 84 'filename' => 'image2.jpg', 85 'headers' => ['Content-Type' => 'image/jpeg'] 86 ], 87 [ 88 'name' => 'album', 89 'contents' => 'Vacation Photos' 90 ] 91 ] 92]); 93``` 94 95## Uploading From a Stream 96 97For large files, you can upload directly from a stream rather than loading the entire file into memory: 98 99```php 100// Open file as a stream 101$stream = fopen('/path/to/large-file.zip', 'r'); 102 103$response = fetch('https://api.example.com/upload', [ 104 'method' => 'POST', 105 'multipart' => [ 106 [ 107 'name' => 'file', 108 'contents' => $stream, 109 'filename' => 'large-file.zip', 110 'headers' => ['Content-Type' => 'application/zip'] 111 ], 112 [ 113 'name' => 'description', 114 'contents' => 'Large file upload' 115 ] 116 ] 117]); 118 119// Don't forget to close the stream 120fclose($stream); 121``` 122 123## File Upload with Progress Tracking 124 125For large file uploads, you might want to track progress. This can be done using Guzzle's progress middleware: 126 127```php 128use Fetch\Http\ClientHandler; 129use GuzzleHttp\Client; 130use GuzzleHttp\HandlerStack; 131use GuzzleHttp\Middleware; 132use GuzzleHttp\Psr7\Utils; 133 134// Create a function to track upload progress 135$progress = function ($totalBytes, $downloadedBytes, $uploadedBytes, $uploadTotal) { 136 if ($uploadTotal > 0) { 137 $percent = round(($uploadedBytes / $uploadTotal) * 100); 138 echo "Upload progress: {$percent}% ({$uploadedBytes}/{$uploadTotal} bytes)\n"; 139 } 140}; 141 142// Create a handler stack with the progress middleware 143$stack = HandlerStack::create(); 144$stack->push(Middleware::tap(null, $progress)); 145 146// Create a custom Guzzle client with the stack 147$guzzleClient = new Client([ 148 'handler' => $stack 149]); 150 151// Create a client handler with the custom client 152$client = ClientHandler::createWithClient($guzzleClient); 153 154// Create a file stream 155$stream = Utils::streamFor(fopen('/path/to/large-file.mp4', 'r')); 156 157// Upload the file 158$response = $client->withMultipart([ 159 [ 160 'name' => 'file', 161 'contents' => $stream, 162 'filename' => 'video.mp4', 163 'headers' => ['Content-Type' => 'video/mp4'] 164 ] 165])->post('https://api.example.com/upload'); 166 167// The progress function will be called during the upload 168``` 169 170## Handling File Upload Errors 171 172File uploads can fail for various reasons. Here's how to handle common errors: 173 174```php 175try { 176 $response = fetch('https://api.example.com/upload', [ 177 'method' => 'POST', 178 'multipart' => [ 179 [ 180 'name' => 'file', 181 'contents' => file_get_contents('/path/to/image.jpg'), 182 'filename' => 'upload.jpg' 183 ] 184 ] 185 ]); 186 187 if ($response->isUnprocessableEntity()) { 188 $errors = $response->json()['errors'] ?? []; 189 foreach ($errors as $field => $messages) { 190 echo "Error with {$field}: " . implode(', ', $messages) . "\n"; 191 } 192 } elseif ($response->status() === 413) { 193 echo "File too large. Maximum size exceeded."; 194 } elseif (!$response->successful()) { 195 echo "Upload failed with status: " . $response->status(); 196 } else { 197 echo "Upload successful!"; 198 } 199} catch (\Fetch\Exceptions\NetworkException $e) { 200 echo "Network error: " . $e->getMessage(); 201} catch (\Fetch\Exceptions\RequestException $e) { 202 echo "Request error: " . $e->getMessage(); 203} catch (\Exception $e) { 204 echo "Error: " . $e->getMessage(); 205} 206``` 207 208## Multipart Form Specifications 209 210When setting up multipart form data, each part needs these elements: 211 212- `name`: The form field name (required) 213- `contents`: The content of the field (required) - can be a string, resource, or StreamInterface 214- `filename`: The filename for file uploads (optional) 215- `headers`: An array of headers for this part (optional) 216 217## File Upload with Authentication 218 219Many APIs require authentication for file uploads: 220 221```php 222$response = fetch('https://api.example.com/upload', [ 223 'method' => 'POST', 224 'token' => 'your-api-token', // Bearer token authentication 225 'multipart' => [ 226 [ 227 'name' => 'file', 228 'contents' => file_get_contents('/path/to/file.jpg'), 229 'filename' => 'upload.jpg' 230 ] 231 ] 232]); 233``` 234 235Or using the ClientHandler: 236 237```php 238$response = ClientHandler::create() 239 ->withToken('your-api-token') 240 ->withMultipart([ 241 [ 242 'name' => 'file', 243 'contents' => file_get_contents('/path/to/file.jpg'), 244 'filename' => 'upload.jpg' 245 ] 246 ]) 247 ->post('https://api.example.com/upload'); 248``` 249 250## Using Enums for Content Types 251 252You can use the `ContentType` enum for specifying content types in your file uploads: 253 254```php 255use Fetch\Enum\ContentType; 256use Fetch\Http\ClientHandler; 257 258$response = ClientHandler::create() 259 ->withMultipart([ 260 [ 261 'name' => 'file', 262 'contents' => file_get_contents('/path/to/document.pdf'), 263 'filename' => 'document.pdf', 264 'headers' => ['Content-Type' => ContentType::PDF->value] 265 ], 266 [ 267 'name' => 'image', 268 'contents' => file_get_contents('/path/to/image.jpg'), 269 'filename' => 'image.jpg', 270 'headers' => ['Content-Type' => ContentType::JPEG->value] 271 ] 272 ]) 273 ->post('https://api.example.com/upload'); 274``` 275 276## File Download 277 278Although not strictly an upload, you might also need to download files: 279 280```php 281$response = fetch('https://api.example.com/files/document.pdf'); 282 283// Save the file to disk 284file_put_contents('downloaded-document.pdf', $response->body()); 285 286// Or get it as a stream 287$stream = $response->blob(); 288$fileContents = stream_get_contents($stream); 289fclose($stream); 290 291// Or get it as a binary string 292$binaryData = $response->arrayBuffer(); 293``` 294 295## Handling Large File Downloads 296 297For large file downloads, you can use streaming: 298 299```php 300use Fetch\Http\ClientHandler; 301 302$client = ClientHandler::create(); 303$response = $client 304 ->withStream(true) // Enable streaming 305 ->get('https://api.example.com/large-files/video.mp4'); 306 307// Open a file to save the download 308$outputFile = fopen('downloaded-video.mp4', 'w'); 309 310// Get the response body as a stream 311$body = $response->getBody(); 312 313// Read the stream in chunks and write to the file 314while (!$body->eof()) { 315 fwrite($outputFile, $body->read(4096)); // Read 4KB at a time 316} 317 318// Close the file 319fclose($outputFile); 320``` 321 322## Asynchronous File Uploads 323 324For large files, you might want to use asynchronous uploads to prevent blocking: 325 326```php 327use function async; 328use function await; 329 330$result = await(async(function() { 331 // Open file as a stream 332 $stream = fopen('/path/to/large-file.zip', 'r'); 333 334 // Upload the file 335 $response = await(async(function() use ($stream) { 336 return fetch('https://api.example.com/upload', [ 337 'method' => 'POST', 338 'multipart' => [ 339 [ 340 'name' => 'file', 341 'contents' => $stream, 342 'filename' => 'large-file.zip' 343 ] 344 ], 345 'timeout' => 300 // 5-minute timeout for large uploads 346 ]); 347 })); 348 349 // Close the stream 350 fclose($stream); 351 352 return $response->json(); 353})); 354 355echo "Upload complete with ID: " . $result['id']; 356``` 357 358## Best Practices 359 3601. **Check File Size**: Verify file sizes before uploading to avoid timeouts or server rejections. 361 3622. **Set Appropriate Timeouts**: Large file uploads may need longer timeouts: 363 364 ```php 365 $response = fetch('https://api.example.com/upload', [ 366 'method' => 'POST', 367 'timeout' => 300, // 5-minute timeout 368 'multipart' => [/* ... */] 369 ]); 370 ``` 371 3723. **Use Streams for Large Files**: Avoid loading large files entirely into memory. 373 3744. **Add Retry Logic**: File uploads are prone to network issues, so add retry logic: 375 376 ```php 377 $response = fetch_client() 378 ->retry(3, 1000) // 3 retries with 1 second initial delay 379 ->retryStatusCodes([408, 429, 500, 502, 503, 504]) 380 ->withMultipart([/* ... */]) 381 ->post('https://api.example.com/upload'); 382 ``` 383 3845. **Validate Before Uploading**: Check file types, sizes, and other constraints before uploading. 385 3866. **Include Progress Tracking**: For large files, provide progress feedback to users. 387 3887. **Log Upload Attempts**: Log uploads for troubleshooting and auditing: 389 390 ```php 391 use Monolog\Logger; 392 use Monolog\Handler\StreamHandler; 393 394 // Create a logger 395 $logger = new Logger('uploads'); 396 $logger->pushHandler(new StreamHandler('logs/uploads.log', Logger::INFO)); 397 398 // Set the logger on the client 399 $client = fetch_client(); 400 $client->setLogger($logger); 401 402 // Make the upload request 403 $response = $client->withMultipart([/* ... */]) 404 ->post('https://api.example.com/upload'); 405 ``` 406 407## Next Steps 408 409- Learn about [Authentication](/guide/authentication) for secured file uploads 410- Explore [Error Handling](/guide/error-handling) for robust upload error management 411- See [Retry Handling](/guide/retry-handling) for handling transient upload failures 412- Check out [Asynchronous Requests](/guide/async-requests) for non-blocking uploads