Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Multipart Upload Bundle Laravel Package

goetas/multipart-upload-bundle

A Symfony bundle for handling chunked, multipart file uploads, letting you stream large files to Amazon S3 using its multipart upload API while integrating with OneupUploaderBundle for resumable, reliable uploads.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the honest framing: goetas/multipart-upload-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no config/multipart-upload.php to publish, and no facade. It ships Symfony DI service definitions, a routing import (@GoetasMultipartUploadBundle/Resources/config/routing.xml), and at least one Symfony FormType — none of which Laravel can consume. So "installing it into a Laravel app" is not the play.

The play is to treat it as two artifacts:

  1. goetas/multipart-upload — the underlying, framework-agnostic PHP library. This is the only piece worth composer require-ing in a Laravel project. It gives you the chunk-upload primitives: an uploaded-file abstraction, a file-token concept, and validators (file identity + byte-range checks). No Symfony coupling.
  2. The bundle — the reference implementation of how to wire those primitives into HTTP: which request fields to accept, how to validate ranges, how to write chunks to a target file safely.

Minimal path for a Laravel developer:

composer require goetas/multipart-upload

Skim the bundle's controller and form type on GitHub (it's ~a handful of files) — that's your spec for the request contract: fields carrying a token, a byte range (start/end), the total size, and the chunk body. Read the library's Validator classes first; they're small and self-contained and tell you exactly what invariants the bundle enforces.

First use case: a large (hundreds of MB to several GB) video or archive upload where you need resume-on-failure, from a JS client like resumable.js, Uppy, or tus. Note the 21 stars / 0 dependents signals: this is a niche, single-maintainer project. Read it, port it, don't depend on it blindly.

Decision gate before writing code: if your chunks ultimately land in S3, skip this entirely and use S3's native multipart upload through Flysystem — you get the same chunking semantics with far less code and no local temp-file coordination.

Implementation Patterns

1. Mirror the wire contract, not the Symfony classes. Reimplement the bundle's controller logic as a plain Laravel controller. The contract is essentially:

POST /uploads/{token}      multipart/form-data or raw body
  token  : stable identifier for this logical upload
  start  : inclusive byte offset of this chunk
  end    : exclusive byte offset
  total  : total expected size (optional, sent on first chunk)
  file   : the chunk bytes

Make each chunk request idempotent — the client will retry after a network drop. Rewriting the same byte range must be harmless.

2. Use the library's validators directly, no container.

use Goetas\MultipartUpload\...\FileValidator;
use Goetas\MultipartUpload\...\RangeValidator;

// instantiate with your own config; these are POJOs, not services
$validator->validate($uploadedFile); // throws on mismatch

Wrap the throw in a try/catch and map to 422 — Laravel has no equivalent of Symfony's validation exception handling for these.

3. Signed, user-scoped tokens. The bundle assumes the token identifies the file; it does not assume authorization. In Laravel, derive the token and sign it:

$token = hash_hmac('sha256', $userId.'|'.$filename.'|'.$size, config('app.key'));

Store a row in uploads (user_id, token, expected_size, received_bytes, status, expires_at). Authenticate the chunk request against that row in a route middleware or a Gate. Never let the client's filename reach the filesystem path.

4. Streaming write, not buffering. Don't $request->file('file')->store(). Open the target with fopen($path, 'c+b'), fseek($fh, $start), then stream_copy_to_stream($request->getContent(true), $fh). For raw-body uploads use getContent(true) to get a stream — it avoids loading the chunk into memory.

5. Lock per token, not per request. Concurrent chunks to the same file will interleave writes. Use Cache::lock("upload:{$token}", 30) (Redis) or flock($fh, LOCK_EX). Update received_bytes inside the lock.

6. Finalize atomically. On the chunk that completes the range, verify total size (and preferably a client-supplied SHA-256), then rename() the temp file into its final location and flip the DB status. rename is atomic on the same filesystem — don't copy+unlink.

7. Clean up on a schedule. Register php artisan uploads:prune in routes/console.php to delete temp files and DB rows past expires_at. The bundle leaves this to you too; orphaned chunks are the #1 operational problem with any chunked-upload implementation.

8. Testing. UploadedFile::fake() doesn't exercise streaming paths. Use $this->call('POST', $uri, [], [], [], [...], $rawBytes) and assert on file contents and offsets. Test out-of-order chunks, duplicate chunks, and a truncated final chunk explicitly.

Gotchas and Tips

  • The FormType is dead weight for you. The bundle's Symfony Form integration (fields, transformers) is unusable in Laravel. Don't try to mimic it; a FormRequest with ['token' => 'required|string', 'start' => 'required|integer|min:0', ...] is the whole job.
  • Per-chunk PHP limits still apply. upload_max_filesize and post_max_size cap each chunk, not the total. The entire point of chunking is to stay under them — size your client chunks at ~5–10 MB and document it. Also check max_file_uploads if you ever send multiple chunks at once.
  • Reverse proxy limits bite first. Nginx client_max_body_size (default 1 MB) returns a 413 before PHP ever runs. This is the single most common "the package is broken" report for chunked uploads.
  • php://input is one-shot. Reading it twice returns empty. Pick one path — $request->file() for multipart, $request->getContent(true) for raw bodies — and stick to it for the whole endpoint.
  • Path traversal. If you're tempted to build the temp path from the client's filename, don't. Use the token (which you signed) as the filename, or a UUID in the DB row.
  • Don't confuse this with S3 multipart. Same vocabulary, different mechanism. If your storage is S3, use Storage::disk('s3') with the native multipart API and skip this library entirely.
  • Replay and expiry. A stolen token lets an attacker overwrite chunks. Bind tokens to a user, give them a short TTL, and reject chunks whose start is beyond received_bytes + allowed_gap.
  • CORS/preflight. Chunked XHR from a browser triggers preflight; make sure OPTIONS on the upload route doesn't hit auth middleware and that Content-Type/Range headers are allowed.
  • Maintenance risk. One maintainer, 21 stars, zero known dependents. Pin the library to an exact version, and prefer copying the ~200 lines of validation logic into app/Support/ over depending on it. You'll own the code either way; at least then you can fix it.
  • Debugging recipe: log (token, start, end, bytes_received) on every chunk; when an upload fails, the gap in the offsets tells you instantly whether it's a client bug, a proxy truncation, or a lock timeout.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect