File uploads
Fetch sends attachments as native multipart requests. Multipart fields are normalized to strings, and native code generates the boundary and Content-Type header.
Upload one file
$request = Fetch::acceptJson()->attach(
'avatar',
$path,
filename: 'avatar.jpg',
mimeType: 'image/jpeg',
);
$requestId = $request->post($url, ['name' => 'Victory']);The field name and path cannot be empty. When omitted, the filename comes from the path and the MIME type defaults to application/octet-stream.
Upload multiple files
Repeated field names are supported:
$request = Fetch::attachMany([
[
'name' => 'photos[]',
'path' => $pathOne,
'filename' => 'one.jpg',
'mimeType' => 'image/jpeg',
],
[
'name' => 'photos[]',
'path' => $pathTwo,
'filename' => 'two.jpg',
'mimeType' => 'image/jpeg',
],
[
'name' => 'document',
'path' => $pdfPath,
'filename' => 'invoice.pdf',
'mimeType' => 'application/pdf',
],
]);
$requestId = $request->post($url, ['title' => 'Documents']);attachMany() validates the entire collection before appending any file. Calling attach() repeatedly provides the same result.
Upload progress
Progress represents the complete multipart request, not an individual file:
use Native\Mobile\Attributes\On;
use Victorycodedev\NativephpFetch\Events\FetchRequestCompleted;
use Victorycodedev\NativephpFetch\Events\FetchUploadProgress;
use Victorycodedev\NativephpFetch\FetchResponse;
#[On(FetchUploadProgress::class)]
public function uploadProgress(
string $requestId,
int $bytesSent,
int $bytesTotal,
float $progress,
): void {
if ($requestId !== $this->requestId) {
return;
}
$this->percentage = (int) round($progress * 100);
}
#[On(FetchRequestCompleted::class)]
public function uploadCompleted(
string $requestId,
int $status,
array $headers,
string $body,
): void {
if ($requestId !== $this->requestId) {
return;
}
$response = FetchResponse::from($requestId, $status, $headers, $body);
if ($response->successful()) {
$this->percentage = 100;
// The upload completed and the server response is available here.
return;
}
$this->error = $response->json('message', 'Upload failed.');
}progress ranges from 0.0 to 1.0. Upload retries reopen their file-backed bodies and progress restarts from zero for each attempt. Uploads finish through FetchRequestCompleted, just like other HTTP requests; there is no separate upload-completed event. Inspect the response status to determine whether the server accepted the upload.