commit c21b9b02f4a1f8ac7be494864338e862dbec9d22 Author: Robert Date: Sun Jun 23 08:28:11 2024 -0400 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8fc0d80 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +uploads/ diff --git a/assets/js/uploading.js b/assets/js/uploading.js new file mode 100644 index 0000000..306980e --- /dev/null +++ b/assets/js/uploading.js @@ -0,0 +1,98 @@ +function isEmptyArray(array) { + return Array.isArray(array) && array.length === 0; +} + +function handleFiles(files) { + const formData = new FormData(); + const validFiles = []; + for (const file of files) { + if (file.size > 0) { + formData.append('files[]', file); + validFiles.push(file); + } else { + console.warn(`The file "${file.name}" is empty and will be skipped.`); + } + } + + if (validFiles.length === 0) { + alert('No valid files to upload. All selected files are empty.'); + return; + } + + fetch('upload.php', { + method: 'POST', + body: formData + }).then(response => { + if (!response.ok) { + throw new Error('Network response was not ok: ' + response.status); + } + return response.json(); + }).then(results => { + let successes = []; + let errors = []; + results.forEach(result => { + if (result.status == "success") { + successes.push(result.message); + } else { + errors.push(result.message); + } + }); + if (!isEmptyArray(successes)) { + const success = successes.map(text => text).join(", "); + alert("Successfully uploaded: " + success); + } + if (!isEmptyArray(errors)) { + const error = errors.map(text => text).join(", "); + alert("Failed on: " + error); + } + }).catch(error => { + if (error.message.startsWith('Network response')) { + alert('Internal Server Error (500): There was a problem with the server.'); + } else { + alert('Error:' + error); + } + }).finally(() => { + for (const key in formData) { + if (formData.hasOwnProperty(key)) { + delete formData[key]; + } + } + while (validFiles.length > 0) { + validFiles.pop(); + } + }); +} + +document.addEventListener('DOMContentLoaded', function() { + const uploadContainer = document.getElementById('uploadContainer'); + const fileInput = document.getElementById('fileInput'); + + uploadContainer.addEventListener('click', () => fileInput.click()); + + uploadContainer.addEventListener('dragover', (e) => { + e.preventDefault(); + uploadContainer.classList.add('dragover'); + }); + + uploadContainer.addEventListener('dragleave', () => { + uploadContainer.classList.remove('dragover'); + }); + + uploadContainer.addEventListener('drop', (e) => { + e.preventDefault(); + uploadContainer.classList.remove('dragover'); + let total = 0; + try { + total = e.dataTransfer.files.length; + } catch (error) { + console.error("Opps" + error.message); + } + if (total > 0) { + handleFiles(e.dataTransfer.files); + } else { + alert("No files dropped. Please try again."); + } + }); + + fileInput.addEventListener('change', (e) => handleFiles(e.target.files)); +}); diff --git a/image_compression.php b/image_compression.php new file mode 100644 index 0000000..1f43e70 --- /dev/null +++ b/image_compression.php @@ -0,0 +1,254 @@ + $min_pixels_before_compression) ? $original / 2 : $original; + } + if ($target < $original) { + return $target; + } + return $original; +} + +function image_compression(string $uploadName, string $fileName, array $images): array { + $rnd_name = generate_unique_filename(); + if (!count($images)) { + return []; + } + $a_of_images = []; + foreach($images as $image) { + $name = $image['name'] ?? false; + $width = $image['width'] ?? false; + $height = $image['height'] ?? false; + $crop = $image['crop'] ?? false; + if ($name === false || $width === false || $height === false) { + continue; + } + $name .= '_' . $rnd_name; + if ($crop) { + $file = do_image_crop($uploadName, $fileName, $name, $width, $height, $rnd_name); + } else { + $file = do_image_compression($uploadName, $fileName, $name, $width, $height); + } + if ($file === false) { + continue; + } + $a_of_images[] = $file; + } + rename_upload($uploadName, $fileName, $rnd_name); + return $a_of_images; +} + +function do_image_compression(string $uploadName, string $fileName, string $newName, int $targetWidth, int $targetHeight): false|string { + + list($originalWidth, $originalHeight, $imageType) = getimagesize($uploadName); + if ($originalWidth == 0 || $originalHeight == 0) { + return $GLOBALS['image_error']; + } + + // File Name without EXT type or Path + $fileNameWithoutExt = pathinfo($fileName, PATHINFO_FILENAME); + + switch ($imageType) { + case IMAGETYPE_JPEG: + $img = imagecreatefromjpeg($uploadName); + $type = '.jpg'; + break; + case IMAGETYPE_PNG: + $img = imagecreatefrompng($uploadName); + $type = '.png'; + break; + case IMAGETYPE_GIF: + $img = imagecreatefromgif($uploadName); + $type = '.gif'; + break; + default: + return false; + } + if ($img === false) { + return false; + } + + $scaledFileName = $GLOBALS['uploadDir'] . $newName . '_' . $fileNameWithoutExt . $type; + + // Calculate half image sizes for scaling + $newWidth = get_size($targetWidth, $originalWidth); + $newHeight = get_size($targetHeight, $originalHeight); + + // Create a new true color image + $scaledImage = imagecreatetruecolor($newWidth, $newHeight); + // Enable transparency + imagealphablending($scaledImage, false); + imagesavealpha($scaledImage, true); + + // Copy and resize the original image into the new image + imagecopyresampled( + $scaledImage, $img, + 0, 0, 0, 0, + $newWidth, $newHeight, + $originalWidth, $originalHeight + ); + + switch ($imageType) { + case IMAGETYPE_JPEG: + $quality = $GLOBALS['JPEG_quality']; + imagejpeg($scaledImage, $scaledFileName, $quality); + break; + case IMAGETYPE_PNG: + $compression =$GLOBALS['PNG_compression']; + $transparentColor = imagecolorallocatealpha($scaledImage, 0, 0, 0, 127); + imagefill($scaledImage, 0, 0, $transparentColor); + imagepng($scaledImage, $scaledFileName, $compression); + break; + case IMAGETYPE_GIF: + $number_of_colors = $GLOBALS['GIF_number_of_colors']; + // Reduce the number of colors to make the file smaller + imagetruecolortopalette($scaledImage, false, $number_of_colors); + imagegif($scaledImage, $scaledFileName); + break; + } + + // Free up memory + imagedestroy($img); + imagedestroy($scaledImage); + + return $newName . '_' . $fileNameWithoutExt . $type; +} + +function do_image_crop(string $uploadName, string $fileName, string $newName, int $targetWidth, int $targetHeight, string $rnd_name): false|string { + + if (! file_exists($GLOBALS['uploadDir'] . 'half_' . $rnd_name . '_' . $fileName)) { + $scaledFileName = $GLOBALS['uploadDir'] . do_image_compression($uploadName, $fileName, "half_" . $rnd_name, $targetWidth, $targetHeight); + } else { + $scaledFileName = $GLOBALS['uploadDir'] . 'half_' . $rnd_name . '_' . $fileName; + } + list($originalWidth, $originalHeight, $imageType) = getimagesize($scaledFileName); + + // File Name without EXT type or Path + $fileNameWithoutExt = pathinfo($fileName, PATHINFO_FILENAME); + + switch ($imageType) { + case IMAGETYPE_JPEG: + $img = imagecreatefromjpeg($scaledFileName); + $type = '.jpg'; + break; + case IMAGETYPE_PNG: + $img = imagecreatefrompng($scaledFileName); + $type = '.png'; + break; + case IMAGETYPE_GIF: + $img = imagecreatefromgif($scaledFileName); + $type = '.gif'; + break; + default: + return false; + } + if ($img === false) { + return false; + } + $croppedFileName = $GLOBALS['uploadDir'] . $newName . '_' . $fileNameWithoutExt . $type; + + // Calculate the coordinates for the cropping + if ($originalWidth <= $targetWidth) { + $cropX = 0; + $targetWidth = $originalWidth; + } else { + $cropX = ($originalWidth - $targetWidth) / 2; // Crop from the center horizontally + } + if ($originalHeight <= $targetHeight) { + $cropY = 0; + $targetHeight = $originalHeight; + } else { + $cropY = ($originalHeight - $targetHeight) / 2; // Crop from the center vertically + } + + switch ($imageType) { + case IMAGETYPE_JPEG: + // Create a new image with the cropped dimensions + $croppedImg = imagecrop($img, ['x' => $cropX, 'y' => $cropY, 'width' => $targetWidth, 'height' => $targetHeight]); + + $quality = $GLOBALS['JPEG_quality']; + imagejpeg($croppedImg, $croppedFileName, $quality); + break; + case IMAGETYPE_PNG: + // Create a new true color image with the desired dimensions + $croppedImg = imagecreatetruecolor($targetWidth, $targetHeight); + + // Enable transparency + imagealphablending($croppedImg, false); + imagesavealpha($croppedImg, true); + + // Allocate a color for the background (fully transparent) + $transparentColor = imagecolorallocatealpha($croppedImg, 0, 0, 0, 127); + imagefill($croppedImg, 0, 0, $transparentColor); + + // Copy the relevant portion of the source image to the new image + imagecopy($croppedImg, $img, 0, 0, $cropX, $cropY, $targetWidth, $targetHeight); + + $compression = $GLOBALS['PNG_compression']; + // Save the cropped image + imagepng($croppedImg, $croppedFileName, $compression); + break; + case IMAGETYPE_GIF: + // Create a new image with the cropped dimensions + $croppedImg = imagecrop($img, ['x' => $cropX, 'y' => $cropY, 'width' => $targetWidth, 'height' => $targetHeight]); + + $number_of_colors = $GLOBALS['GIF_number_of_colors']; + // Reduce the number of colors to make the file smaller + imagetruecolortopalette($croppedImg, false, $number_of_colors); + + imagegif($croppedImg, $croppedFileName); + break; + } + + // Free up memory + imagedestroy($img); + imagedestroy($croppedImg); + + return $newName . '_' . $fileNameWithoutExt . $type; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..1166235 --- /dev/null +++ b/index.html @@ -0,0 +1,39 @@ + + + + + + Drag and Drop Multiple File Upload + + + +
+ Drag and drop image files here or click to select files + +
+ + + + diff --git a/upload.php b/upload.php new file mode 100644 index 0000000..fb1bbf2 --- /dev/null +++ b/upload.php @@ -0,0 +1,84 @@ +'half', 'width'=>0, 'height'=>0, 'crop'=>false], + ['name'=>'hd', 'width'=>1920, 'height'=>1080, 'crop'=>false], + ['name'=>'thumbnail', 'width'=>300, 'height'=>300, 'crop'=>false], + //['name'=>'crop', 'width'=>300, 'height'=>300, 'crop'=>true], +]; +// Set the quality (0-100) +$JPEG_quality = 75; // Adjust this value as needed +// Set the compression level (0-9) +$PNG_compression = 9; // Adjust this value as needed +$GIF_number_of_colors = 128; // Adjust number of Colors as needed + +/** + * nano /etc/php/8.3/apache2/php.ini + * edit php.ini for the following: + */ +ini_set('upload_max_filesize', '6M'); // Adjust upload limits +ini_set('post_max_size', '6M'); +ini_set('max_file_uploads', '15'); +ini_set('max_execution_time', '90'); // 90 seconds = 1.5 minutes +ini_set('max_input_time', '90'); // 1.5 minutes + +require_once "image_compression.php"; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (isset($_FILES['files'])) { + $files = $_FILES['files']; + $responses = []; + + // Ensure the uploads directory exists + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0775, true); + } + + for ($i = 0; $i < count($files['name']); $i++) { + $fileName = basename($files['name'][$i]); + $fileTmpName = $files['tmp_name'][$i]; + $fileSize = $files['size'][$i]; + $fileType = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); + $uploadFile = $uploadDir . $fileName; + + // Validate file type (only images allowed) + if (!in_array($fileType, $validTypes)) { + $responses[] = ['status' => 'error', 'message' => "Invalid file type for {$fileName}. Only images are allowed."]; + @unlink($fileTmpName); + continue; + } + + // Validate file size (limit to 5MB) + if ($fileSize > $maxFileSize) { + $responses[] = ['status' => 'error', 'message' => "File size for $fileName exceeds the limit of 5MB."]; + @unlink($fileTmpName); + continue; + } + + // Move the uploaded file to the server + if (move_uploaded_file($fileTmpName, $uploadFile)) { + $compressed_files = image_compression($uploadFile, $fileName, $GLOBALS['a_crops']); + if (count($compressed_files)) { + $responses[] = ['status' => 'success', 'message' => "$fileName"]; + foreach($compressed_files as $file) { + $save_to_DB = $uploadDir . $file; + // HANDLE $save_to_DB to save to Database + } + } else { + $responses[] = ['status' => 'error', 'message' => "Failed to Scale image$fileName."]; + } + } else { + $responses[] = ['status' => 'error', 'message' => "Failed to upload $fileName."]; + } + } + + echo json_encode($responses); + } else { + echo json_encode([['status' => 'error', 'message' => 'No files were uploaded.']]); + } +} else { + echo json_encode([['status' => 'error', 'message' => 'Invalid request method.']]); +}