commit
c21b9b02f4
@ -0,0 +1 @@ |
||||
uploads/ |
||||
@ -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)); |
||||
}); |
||||
@ -0,0 +1,254 @@ |
||||
<?php |
||||
$min_pixels_before_compression = 999; |
||||
|
||||
function generate_unique_filename(string $prefix = ''): string { |
||||
$timestamp = microtime(true); // Current timestamp with microseconds |
||||
$random = mt_rand(); // Random number (better than rand() for uniqueness) |
||||
$hash = md5($timestamp . $random); // MD5 hash of timestamp and random number |
||||
|
||||
// Combine prefix (if provided) with the hash |
||||
$filename = $prefix . $hash; |
||||
|
||||
return $filename; |
||||
} |
||||
|
||||
/** |
||||
* Make sure uploads (LIKE Images, etc...) do NOT run PHP code!! |
||||
* Checks for PHP tags inside of file. |
||||
* @param string $file |
||||
* @return bool true if PHP was found |
||||
*/ |
||||
function file_contains_php(string $file): bool { |
||||
$file_handle = fopen($file, "r"); |
||||
while (!feof($file_handle)) { |
||||
$line = fgets($file_handle); |
||||
$pos = strpos($line, '<?php'); |
||||
if ($pos !== false) { |
||||
fclose($file_handle); |
||||
return true; |
||||
} |
||||
} |
||||
fclose($file_handle); |
||||
return false; |
||||
} |
||||
|
||||
function rename_upload(string $uploadName, string $fileName, false|string $rndFile): false|string { |
||||
if (file_contains_php($uploadName)) { |
||||
@unlink($uploadName); // stop attacks by a Delete of the PHP Image file!! |
||||
return false; |
||||
} |
||||
if ($rndFile === false) { |
||||
return false; |
||||
} |
||||
$success = rename($uploadName, $GLOBALS['uploadDir'] . 'orignal_' . $rndFile . '_' . $fileName); |
||||
return ($success) ? $GLOBALS['uploadDir'] . 'orignal_' . $rndFile . $fileName . '_': false; |
||||
} |
||||
|
||||
function get_size(int $target, int $original): int { |
||||
$min_pixels_before_compression = $GLOBALS['min_pixels_before_compression']; |
||||
if ($target == 0) { |
||||
return ($original > $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; |
||||
} |
||||
@ -0,0 +1,39 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<title>Drag and Drop Multiple File Upload</title> |
||||
<style> |
||||
body { |
||||
font-family: Arial, sans-serif; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
height: 100vh; |
||||
margin: 0; |
||||
} |
||||
.upload-container { |
||||
width: 300px; |
||||
padding: 20px; |
||||
border: 2px dashed #ccc; |
||||
text-align: center; |
||||
cursor: pointer; |
||||
} |
||||
.upload-container.dragover { |
||||
background-color: #f0f0f0; |
||||
} |
||||
</style> |
||||
</head> |
||||
<body> |
||||
<div class="upload-container" id="uploadContainer"> |
||||
Drag and drop image files here or click to select files |
||||
<input type="file" id="fileInput" style="display: none;" accept="image/*" multiple> |
||||
</div> |
||||
<script src="./assets/js/uploading.js"></script> |
||||
<noscript> |
||||
<h1>Error! :</h1> |
||||
<p>This website requires JavaScript to function properly.</p> |
||||
</noscript> |
||||
</body> |
||||
</html> |
||||
@ -0,0 +1,84 @@ |
||||
<?php |
||||
// Upload Directory |
||||
$uploadDir = 'uploads/'; |
||||
$maxFileSize = 5 * 1024 * 1024; // 5MB in bytes |
||||
$validTypes = ['jpg', 'jpeg', 'png', 'gif']; // Note .SVG is dangerious |
||||
$a_crops = [ |
||||
['name'=>'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.']]); |
||||
} |
||||
Loading…
Reference in new issue