Installation:
composer require robtimus/multipart
Add to composer.json if using a monorepo or custom package setup.
First Use Case: File Upload Handling Handle a file upload in a Laravel controller:
use Robtimus\Multipart\MultipartFormData;
public function upload(Request $request)
{
$multipart = new MultipartFormData();
$multipart->addFile(
'user_file',
$request->file('file')->getClientOriginalName(),
fopen($request->file('file')->getPathname(), 'r'),
$request->file('file')->getMimeType(),
$request->file('file')->getSize()
);
$multipart->addValue('user_id', auth()->id());
// Stream to cURL or buffer for storage
$buffered = $multipart->buffer();
// Save $buffered to database or send via API
}
Key Classes to Know:
MultipartFormData: For multipart/form-data (e.g., HTML forms, API uploads).MultipartRelated: For embedding resources (e.g., HTML emails with images).MultipartMixed: For email attachments or mixed content.MultipartAlternative: For fallbacks (e.g., HTML/plain-text emails).Where to Look First:
multipart/form-data examples.Workflow:
MultipartFormData to construct payloads for APIs or custom storage.$multipart = new MultipartFormData();
foreach ($request->file('files') as $file) {
$multipart->addFile(
'files[]', // Note the `[]` for multiple files
$file->getClientOriginalName(),
fopen($file->getPathname(), 'r'),
$file->getMimeType(),
$file->getSize()
);
}
$multipart->addValue('metadata', json_encode($request->metadata));
// Stream to cURL or buffer
$buffer = $multipart->buffer();
$client->post('https://api.example.com/upload', [
'headers' => ['Content-Type' => $multipart->getContentType()],
'body' => $buffer,
]);
Laravel-Specific Tip:
Laravel\Fortify or Laravel\Jetstream for authenticated uploads:
$multipart->addValue('user_id', auth()->user()->id);
Workflow:
MultipartMixed for emails with attachments or MultipartAlternative for HTML/text fallbacks.$mixed = new MultipartMixed();
$alternative = new MultipartAlternative();
$alternative->addPart($htmlContent, 'text/html');
$alternative->addPart($textContent, 'text/plain');
$mixed->addMultipart($alternative);
// Embedded image (inline)
$related = new MultipartRelated();
$related->addPart($htmlContent, 'text/html');
$related->addInlineFile(
'logo',
'logo.png',
file_get_contents('logo.png'),
'image/png',
contentTransferEncoding: 'base64'
);
$mixed->addMultipart($related);
// Attachment
$mixed->addAttachment(
'report.pdf',
file_get_contents('report.pdf'),
'application/pdf'
);
// Send via Laravel Mail
Mail::raw((string)$mixed, function ($message) {
$message->to('user@example.com')
->subject('Your Report');
});
Tip:
(string)$multipart to cast to a string (buffers automatically).Swift_Mime or Symfony Mailer.Workflow:
$multipart = new MultipartFormData();
$multipart->addFile(
'document',
'contract.pdf',
fopen('contract.pdf', 'r'),
'application/pdf'
);
$ch = curl_init('https://api.example.com/upload');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_UPLOAD => true,
CURLOPT_READFUNCTION => [$multipart, 'curlRead'],
CURLOPT_HTTPHEADER => [
'Content-Type: ' . $multipart->getContentType(),
'Authorization: Bearer ' . $token,
],
]);
curl_exec($ch);
curl_close($ch);
Pattern:
$multipart->finish() before streaming.fopen for files and close resources manually (or wrap in a finally block).Workflow:
FormRequest to validate multipart payloads.use Robtimus\Multipart\MultipartFormData;
public function rules()
{
return [
'file' => 'required|file|mimes:pdf,docx|max:10240', // Laravel validation
'metadata' => 'required|json',
];
}
public function withValidator($validator)
{
$validator->after(function ($validator) {
if ($this->has('file')) {
$multipart = new MultipartFormData();
$multipart->addFile(
'file',
$this->file('file')->getClientOriginalName(),
fopen($this->file('file')->getPathname(), 'r'),
$this->file('file')->getMimeType()
);
$buffer = $multipart->buffer();
// Custom validation logic (e.g., check file content)
}
});
}
Workflow:
Use MultipartFormData with Laravel’s UploadComponent or custom JavaScript for progress tracking.
Example:
// Frontend (JavaScript)
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('user_id', userId);
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
console.log(`Uploaded ${e.loaded}/${e.total}`);
});
xhr.open('POST', '/upload', true);
xhr.send(formData);
// Backend (Laravel)
public function upload(Request $request)
{
$multipart = new MultipartFormData();
$multipart->addFile(
'file',
$request->file('file')->getClientOriginalName(),
fopen($request->file('file')->getPathname(), 'r'),
$request->file('file')->getMimeType()
);
// Process or store $multipart
}
Resource Leaks:
addFile/addPart.finally blocks or wrap in a try-catch:
$resource = fopen('large_file.bin', 'r');
try {
$multipart->addFile('file', $resource, 'application/octet-stream', filesize('large_file.bin'));
$buffer = $multipart->buffer();
} finally {
fclose($resource);
}
Buffering vs. Streaming:
buffer() before finish() may miss parts.$multipart->finish() before buffering or streaming:
$multipart->addFile(...);
$multipart->finish(); // Critical!
$buffer = $multipart->buffer();
Content-Length Mismatches:
Content-Length is missing or incorrect.$headers = ['Content-Type: ' . $multipart->getContentType()];
if (($length = $multipart->getContentLength()) >= 0) {
$headers[] = 'Content-Length: ' . $length;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
Multiple Files with Same Name:
How can I help you explore Laravel packages today?