Handle MultipleUpload file uploads with plain PHP using the
$_FILES superglobal and
move_uploaded_file.
No frameworks required -- works on any PHP hosting environment.
Save this as upload.php.
It receives the file via standard multipart form POST and moves it to the uploads directory.
<?php
// upload.php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, X-Upload-Id, X-Chunk-Index, X-Chunk-Count, X-File-Name, X-File-Size");
header("Content-Type: application/json");
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") {
http_response_code(204);
exit;
}
$uploadDir = __DIR__ . "/uploads/";
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
if ($_SERVER["REQUEST_METHOD"] !== "POST" || empty($_FILES["file"])) {
http_response_code(400);
echo json_encode(["success" => false, "error" => "No file uploaded"]);
exit;
}
$file = $_FILES["file"];
if ($file["error"] !== UPLOAD_ERR_OK) {
http_response_code(400);
echo json_encode(["success" => false, "error" => "Upload error code: " . $file["error"]]);
exit;
}
$fileGuid = bin2hex(random_bytes(16));
$fileGuid = sprintf("%s-%s-%s-%s-%s",
substr($fileGuid, 0, 8),
substr($fileGuid, 8, 4),
substr($fileGuid, 12, 4),
substr($fileGuid, 16, 4),
substr($fileGuid, 20)
);
$ext = pathinfo($file["name"], PATHINFO_EXTENSION);
$destPath = $uploadDir . $fileGuid . ($ext ? "." . $ext : "");
if (!move_uploaded_file($file["tmp_name"], $destPath)) {
http_response_code(500);
echo json_encode(["success" => false, "error" => "Failed to save file"]);
exit;
}
echo json_encode([
"success" => true,
"fileGuid" => $fileGuid,
"fileName" => $file["name"],
"fileSize" => $file["size"]
]);
Save this as upload-chunk.php.
Each chunk arrives as a raw body with custom headers. Chunks are stored in a temporary directory
and assembled when the last chunk arrives.
<?php
// upload-chunk.php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, X-Upload-Id, X-Chunk-Index, X-Chunk-Count, X-File-Name, X-File-Size");
header("Content-Type: application/json");
if ($_SERVER["REQUEST_METHOD"] === "OPTIONS") {
http_response_code(204);
exit;
}
$uploadDir = __DIR__ . "/uploads/";
$chunkDir = __DIR__ . "/uploads/chunks/";
$uploadId = $_SERVER["HTTP_X_UPLOAD_ID"] ?? "";
$chunkIndex = (int)($_SERVER["HTTP_X_CHUNK_INDEX"] ?? 0);
$chunkCount = (int)($_SERVER["HTTP_X_CHUNK_COUNT"] ?? 0);
$fileName = $_SERVER["HTTP_X_FILE_NAME"] ?? "unknown";
$fileSize = (int)($_SERVER["HTTP_X_FILE_SIZE"] ?? 0);
if (!$uploadId || !$chunkCount) {
http_response_code(400);
echo json_encode(["success" => false, "error" => "Missing chunk headers"]);
exit;
}
$sessionDir = $chunkDir . $uploadId . "/";
if (!is_dir($sessionDir)) {
mkdir($sessionDir, 0755, true);
}
// Save chunk from raw input
$chunkData = file_get_contents("php://input");
file_put_contents($sessionDir . "chunk_" . $chunkIndex, $chunkData);
// Count received chunks
$received = count(glob($sessionDir . "chunk_*"));
if ($received === $chunkCount) {
// Assemble final file
$fileGuid = bin2hex(random_bytes(16));
$fileGuid = sprintf("%s-%s-%s-%s-%s",
substr($fileGuid, 0, 8),
substr($fileGuid, 8, 4),
substr($fileGuid, 12, 4),
substr($fileGuid, 16, 4),
substr($fileGuid, 20)
);
$ext = pathinfo($fileName, PATHINFO_EXTENSION);
$finalPath = $uploadDir . $fileGuid . ($ext ? "." . $ext : "");
$output = fopen($finalPath, "wb");
for ($i = 0; $i < $chunkCount; $i++) {
$chunkPath = $sessionDir . "chunk_" . $i;
$data = file_get_contents($chunkPath);
fwrite($output, $data);
unlink($chunkPath);
}
fclose($output);
rmdir($sessionDir);
echo json_encode([
"success" => true,
"fileGuid" => $fileGuid,
"fileName" => $fileName,
"fileSize" => $fileSize
]);
exit;
}
echo json_encode(["success" => true, "chunksReceived" => $received]);
Both endpoints must return this JSON structure on successful upload completion.
{
"success": true,
"fileGuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"fileName": "photo.jpg",
"fileSize": 204800
}
Initialize MultipleUpload on the client, pointing to your PHP endpoints.
<link rel="stylesheet" href="multipleupload.css" />
<div id="uploader"></div>
<script src="multipleupload.js"></script>
<script>
var uploader = new MultipleUpload("#uploader", {
uploadUrl: "/upload.php",
chunkUploadUrl: "/upload-chunk.php",
chunkSize: 2 * 1024 * 1024, // 2 MB chunks
onFileUploaded: function (file, response) {
console.log("Uploaded:", response.fileName, response.fileGuid);
}
});
</script>