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