Works with any backend npm install Zero dependencies
← All Backend Guides

Laravel

Handle MultipleUpload file uploads with Laravel using the built-in Request file handling and storage facade. This guide covers both standard multipart uploads and chunked uploads.

Routes

Register the upload routes in routes/api.php.

// routes/api.php
use App\Http\Controllers\UploadController;

Route::post("/upload", [UploadController::class, "upload"]);
Route::post("/upload-chunk", [UploadController::class, "uploadChunk"]);

Multipart Form Upload

The controller receives the file through Laravel's Request object and stores it with a unique GUID.

<?php
// app/Http/Controllers/UploadController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;

class UploadController extends Controller
{
    public function upload(Request $request)
    {
        $request->validate([
            "file" => "required|file|max:204800" // 200 MB
        ]);

        $file = $request->file("file");
        $fileGuid = (string) Str::uuid();
        $ext = $file->getClientOriginalExtension();
        $storedName = $fileGuid . ($ext ? ".{$ext}" : "");

        $file->storeAs("uploads", $storedName, "local");

        return response()->json([
            "success"  => true,
            "fileGuid" => $fileGuid,
            "fileName" => $file->getClientOriginalName(),
            "fileSize" => $file->getSize(),
        ]);
    }
}

Chunked Upload

Each chunk arrives as a raw body with custom headers. The controller stores chunks in a temporary directory and assembles them when the final chunk arrives.

// Add to UploadController
public function uploadChunk(Request $request)
{
    $uploadId   = $request->header("X-Upload-Id");
    $chunkIndex = (int) $request->header("X-Chunk-Index");
    $chunkCount = (int) $request->header("X-Chunk-Count");
    $fileName   = $request->header("X-File-Name");
    $fileSize   = (int) $request->header("X-File-Size");

    if (!$uploadId || !$chunkCount) {
        return response()->json(["success" => false, "error" => "Missing headers"], 400);
    }

    $chunkDir = "chunks/{$uploadId}";
    $chunkData = $request->getContent();
    Storage::disk("local")->put("{$chunkDir}/chunk_{$chunkIndex}", $chunkData);

    // Count received chunks
    $received = count(Storage::disk("local")->files($chunkDir));

    if ($received === $chunkCount) {
        // Assemble final file
        $fileGuid = (string) Str::uuid();
        $ext = pathinfo($fileName, PATHINFO_EXTENSION);
        $finalName = $fileGuid . ($ext ? ".{$ext}" : "");
        $finalPath = storage_path("app/uploads/{$finalName}");

        $output = fopen($finalPath, "wb");
        for ($i = 0; $i < $chunkCount; $i++) {
            $data = Storage::disk("local")->get("{$chunkDir}/chunk_{$i}");
            fwrite($output, $data);
        }
        fclose($output);

        // Clean up chunks
        Storage::disk("local")->deleteDirectory($chunkDir);

        return response()->json([
            "success"  => true,
            "fileGuid" => $fileGuid,
            "fileName" => $fileName,
            "fileSize" => $fileSize,
        ]);
    }

    return response()->json(["success" => true, "chunksReceived" => $received]);
}

CORS Configuration

Update config/cors.php to allow the custom chunk headers.

// config/cors.php
return [
    "paths" => ["api/*"],
    "allowed_origins" => ["*"],
    "allowed_methods" => ["POST", "OPTIONS"],
    "allowed_headers" => [
        "Content-Type", "X-Upload-Id", "X-Chunk-Index",
        "X-Chunk-Count", "X-File-Name", "X-File-Size"
    ],
    "max_age" => 86400,
];

Expected JSON Response

Both endpoints must return this JSON structure on successful upload completion.

{
  "success": true,
  "fileGuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "fileName": "photo.jpg",
  "fileSize": 204800
}

Client-Side JavaScript

Initialize MultipleUpload on the client, pointing to your Laravel API endpoints.

<link rel="stylesheet" href="multipleupload.css" />
<div id="uploader"></div>
<script src="multipleupload.js"></script>
<script>
  var uploader = new MultipleUpload("#uploader", {
    uploadUrl: "/api/upload",
    chunkUploadUrl: "/api/upload-chunk",
    chunkSize: 2 * 1024 * 1024, // 2 MB chunks
    onFileUploaded: function (file, response) {
      console.log("Uploaded:", response.fileName, response.fileGuid);
    }
  });
</script>