Skip to Content
Housr PortalServicesFile Uploads (Uppy + S3)

File Uploads (Uppy + S3)

The Portal uses Uppy (client-side upload library) with a custom S3 Companion implementation for direct-to-S3 multipart file uploads.

Architecture

Browser (Uppy Dashboard) -> Portal Companion routes -> AWS S3 (presigned URLs)

Uppy in the browser communicates with the Portal’s companion endpoints, which generate presigned S3 URLs. Files are uploaded directly from the browser to S3, avoiding the Portal server as a middleman.

Client-Side (Uppy)

Packages (from package.json):

  • @uppy/core (v5.1.1) — Core upload engine
  • @uppy/dashboard (v5.0.3) — Upload UI widget
  • @uppy/drag-drop (v5.0.2) — Drag and drop support
  • @uppy/aws-s3 (v5.0.2) — AWS S3 plugin for multipart uploads

The Uppy Dashboard component provides the user-facing upload interface with drag-and-drop, progress bars, and file preview.

Server-Side Companion

UppyCompanionService

File: app/Services/UppyCompanionService.php

A service class that wraps the AWS S3 SDK to handle presigned URL generation and multipart upload management.

Constructor dependencies:

  • S3Client — AWS S3 client instance
  • string $s3Bucket — Target bucket name

Default expiry: 60 minutes for presigned URLs

Methods

getPresignedUrl(string $uploadKey, string $type): array

Generates a presigned PUT URL for simple (non-multipart) uploads.

Returns: { url, fields, headers }

createMultipartUpload(string $fileName, string $type, array $metadata): array

Initiates an S3 multipart upload.

Returns: { uploadId, key }

listPartsPage(string $uploadId, int $partIndex = 0): Collection

Lists all uploaded parts for a multipart upload. Handles pagination automatically with a safety limit of 1000 iterations.

presignPartURL(string $uploadKey, string $uploadId, int $partNumber): string

Generates a presigned URL for uploading a single part of a multipart upload. Uses SHA1 checksum algorithm.

completeMultipartUpload(string $uploadKey, string $uploadId, array $parts): Result

Completes a multipart upload by assembling all parts.

abortMultipartUpload(string $uploadKey, string $uploadId): void

Aborts an in-progress multipart upload and cleans up uploaded parts.

Companion Routes

Defined in: routes/web.php (no auth middleware — accessible publicly)

MethodPathControllerPurpose
POSTcompanion/s3/paramsParamsController@indexGet presigned URL params
OPTIONScompanion/s3/multipartMultipartController@optionsCORS preflight
POSTcompanion/s3/multipartMultipartController@storeCreate multipart upload
GETcompanion/s3/multipart/{uploadId}MultipartController@showGet multipart upload info
DELETEcompanion/s3/multipart/{uploadId}MultipartController@destroyAbort multipart upload
GETcompanion/s3/multipart/{uploadId}/{partNumber}SignpartController@showGet presigned URL for part
POSTcompanion/s3/multipart/{uploadId}/completeCompleteMultipartController@postComplete multipart upload

Controllers are in app/Http/Controllers/Uppy/.

S3 Buckets

File uploads target different S3 buckets depending on the use case (configured in config/filesystems.php):

DiskBucketUse Case
public_imageshousr-public-imagesHouse photos, agent logos, event banners
agent_documentshousr-agent-documentsAgent verification documents
public_mischousr-public-miscCSV exports, miscellaneous files

Filament File Uploads

The Filament admin panel uses Filament’s built-in FileUpload component, which handles uploads differently:

  • Files are uploaded through PHP to the server first
  • Then saved to S3 via the configured Flysystem disk
  • Example: AgentResource saves logos to the public_images disk with random filenames
  • Example: EventForm saves banner/modal images to public_images under events/{slug}/

This is separate from the Uppy companion flow and does not use presigned URLs.

Security Note

The companion routes at companion/s3/* do not have authentication middleware applied. They are publicly accessible. The presigned URL approach limits exposure since the URLs are time-limited (60 minutes) and scoped to specific S3 keys.

Last updated on