Files
site-builder/api/image-resize.php

194 lines
5.4 KiB
PHP
Raw Normal View History

<?php
/**
* Image Resize/Crop API
*
* Usage:
* POST /api/image-resize.php
* Parameters:
* - image: uploaded file (multipart) OR url (string)
* - width: target width (int)
* - height: target height (int)
* - mode: 'resize' | 'crop' | 'fit' (default: resize)
* - quality: 1-100 (default: 85)
* - format: 'jpg' | 'png' | 'webp' (default: auto)
*
* Returns: JSON { success: true, url: "path/to/resized.jpg", width: N, height: N }
*/
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
// Configuration
$uploadDir = __DIR__ . '/../uploads/';
$outputDir = __DIR__ . '/../uploads/resized/';
$maxFileSize = 10 * 1024 * 1024; // 10MB
// Create directories
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
if (!is_dir($outputDir)) mkdir($outputDir, 0755, true);
// Get parameters
$width = intval($_POST['width'] ?? 0);
$height = intval($_POST['height'] ?? 0);
$mode = $_POST['mode'] ?? 'resize';
$quality = intval($_POST['quality'] ?? 85);
$format = $_POST['format'] ?? 'auto';
if ($width <= 0 && $height <= 0) {
echo json_encode(['error' => 'Width or height required']);
exit;
}
$quality = max(1, min(100, $quality));
// Get source image
$sourcePath = null;
$cleanup = false;
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
if ($_FILES['image']['size'] > $maxFileSize) {
echo json_encode(['error' => 'File too large (max 10MB)']);
exit;
}
$sourcePath = $_FILES['image']['tmp_name'];
} elseif (!empty($_POST['url'])) {
$url = filter_var($_POST['url'], FILTER_VALIDATE_URL);
if (!$url) {
echo json_encode(['error' => 'Invalid URL']);
exit;
}
$tempFile = tempnam(sys_get_temp_dir(), 'img_');
$content = @file_get_contents($url);
if ($content === false) {
echo json_encode(['error' => 'Could not download image']);
exit;
}
file_put_contents($tempFile, $content);
$sourcePath = $tempFile;
$cleanup = true;
} else {
echo json_encode(['error' => 'No image provided']);
exit;
}
// Detect image type
$info = @getimagesize($sourcePath);
if (!$info) {
if ($cleanup) unlink($sourcePath);
echo json_encode(['error' => 'Invalid image file']);
exit;
}
$srcWidth = $info[0];
$srcHeight = $info[1];
$mime = $info['mime'];
// Create source image resource
switch ($mime) {
case 'image/jpeg': $srcImg = imagecreatefromjpeg($sourcePath); break;
case 'image/png': $srcImg = imagecreatefrompng($sourcePath); break;
case 'image/gif': $srcImg = imagecreatefromgif($sourcePath); break;
case 'image/webp': $srcImg = imagecreatefromwebp($sourcePath); break;
default:
if ($cleanup) unlink($sourcePath);
echo json_encode(['error' => 'Unsupported image type: ' . $mime]);
exit;
}
if ($cleanup) unlink($sourcePath);
// Calculate dimensions
$dstWidth = $width;
$dstHeight = $height;
$srcX = 0;
$srcY = 0;
$cropWidth = $srcWidth;
$cropHeight = $srcHeight;
if ($mode === 'resize') {
if ($dstWidth <= 0) $dstWidth = intval($srcWidth * ($dstHeight / $srcHeight));
if ($dstHeight <= 0) $dstHeight = intval($srcHeight * ($dstWidth / $srcWidth));
} elseif ($mode === 'fit') {
if ($dstWidth <= 0) $dstWidth = $srcWidth;
if ($dstHeight <= 0) $dstHeight = $srcHeight;
$ratio = min($dstWidth / $srcWidth, $dstHeight / $srcHeight);
$dstWidth = intval($srcWidth * $ratio);
$dstHeight = intval($srcHeight * $ratio);
} elseif ($mode === 'crop') {
if ($dstWidth <= 0) $dstWidth = $dstHeight;
if ($dstHeight <= 0) $dstHeight = $dstWidth;
$ratio = max($dstWidth / $srcWidth, $dstHeight / $srcHeight);
$cropWidth = intval($dstWidth / $ratio);
$cropHeight = intval($dstHeight / $ratio);
$srcX = intval(($srcWidth - $cropWidth) / 2);
$srcY = intval(($srcHeight - $cropHeight) / 2);
}
// Create destination image
$dstImg = imagecreatetruecolor($dstWidth, $dstHeight);
// Preserve transparency for PNG
if ($mime === 'image/png' || $format === 'png') {
imagealphablending($dstImg, false);
imagesavealpha($dstImg, true);
}
// Resample
imagecopyresampled($dstImg, $srcImg, 0, 0, $srcX, $srcY, $dstWidth, $dstHeight, $cropWidth, $cropHeight);
// Determine output format
if ($format === 'auto') {
$format = match($mime) {
'image/png' => 'png',
'image/gif' => 'png',
'image/webp' => 'webp',
default => 'jpg'
};
}
// Generate output filename
$filename = 'img_' . uniqid() . '_' . $dstWidth . 'x' . $dstHeight . '.' . $format;
$outputPath = $outputDir . $filename;
// Save
switch ($format) {
case 'jpg':
case 'jpeg':
imagejpeg($dstImg, $outputPath, $quality);
break;
case 'png':
imagepng($dstImg, $outputPath, intval(9 - ($quality / 100 * 9)));
break;
case 'webp':
imagewebp($dstImg, $outputPath, $quality);
break;
}
// Cleanup
imagedestroy($srcImg);
imagedestroy($dstImg);
// Return result
$relPath = 'uploads/resized/' . $filename;
echo json_encode([
'success' => true,
'url' => $relPath,
'width' => $dstWidth,
'height' => $dstHeight,
'format' => $format,
'size' => filesize($outputPath)
]);