i wanted to create a thumbnail with specific custom width & height. The function am using only create a thumbnail with a maximum set width/height.
How do i tweak the below function to give me a defined width/height e.g 50x50, 75x75, 100x100.
$original_photo = "photos/photo.extension";
$newcopy = "photos/thumbnails/photo.extension";
$copy_w = 50;
$copy_h = 50;
$extension = explode('.', 'photo.extension');
$extension = end($extension);
function create_thumbnail($original_photo, $newcopy, $copy_w, $copy_h, $extension) {
list($original_w, $original_h) = getimagesize($original_photo);
$scale_ratio = $original_w / $original_h;
if (($copy_w / $copy_h) > $scale_ratio) {
$copy_w = $copy_h * $scale_ratio;
} else {
$copy_h = $copy_w / $scale_ratio;
}
$img = '';
if ($extension == 'gif') {
$img = imagecreatefromgif($original_photo);
} elseif ($extension == 'png') {
$img = imagecreatefrompng($original_photo);
} else {
$img = imagecreatefromjpeg($original_photo);
}
$true_color = imagecreatetruecolor($copy_w, $copy_h);
imagecopyresampled($true_color, $img, 0, 0, 0, 0, $copy_w, $copy_h, $original_w, $original_h);
if (imagejpeg($true_color, $newcopy, 80) == true) {
return true;
} else {
return false;
}
}
Working with images in PHP/GD can be a pain. There are a lot of edge cases, particularly when transparent PNG/GIFs are manipulated.
If possible, I shamelessly recommend a library I wrote to handle things like this: SimpleImage 3.0
Using SimpleImage, you can achieve the desired effect with the following code:
// Load the image from image.jpg
$image = new \claviska\SimpleImage('image.jpg');
// Create a 50x50 thumbnail, convert to PNG, and write to thumbnail.png
$image->thumbnail(50, 50)->toFile('thumbnail.png', 'image/png');
See this page for more details on how the thumbnail method works and available arguments.
I am making an image upload function which I can re-use in my code, which has to be 100% secure. Please tell me if you can spot and security holes in my initial code;
function Upload($file)
{
list($width,$height,$type,$attr) = getimagesize($file);
$mime = image_type_to_mime_type($type);
if(($mime != "image/jpeg") && ($mime != "image/pjpeg") && ($mime != "image/png"))
{
return 'Error3: Upload file type un-recognized. Only .JPG or .PNG images allowed';
}else{
$Newname = md5('sillysalt'.time());
if (move_uploaded_file($file, 'images/'.$Newname.$type))
{
return 'Uploaded!';
}else{
return 'Server Error!';
}
}
}
UPDATE This is how far I've gotten with your help and some research, please tell me what you think. I don't mind much about the speed, for me it's all about being 100% secure, or as close to.
function Upload($file)
{
list($width,$height,$type,$attr) = getimagesize($file);
$mime = image_type_to_mime_type($type);
$folder = 'images/';
// mime checks add a layer of security that keeps out less sophisticated attackers
if(($mime != "image/jpeg") && ($mime != "image/pjpeg") && ($mime != "image/png"))
{
return 'Error3: Upload file type un-recognized. Only .JPG or .PNG images allowed';
}else{
// If the file has no width its not a valid image
if(!$width)
{
$Newname = md5('sillysalt'.time());
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime2 = finfo_file($finfo, $folder.$Newname);
// Should I remove this second mime check? since the info comes form the same spoofable source in the image
if(($mime != "image/jpeg") && ($mime != "image/pjpeg") && ($mime != "image/png"))
{
$fileType = exif_imagetype($file);
$allowed = array(IMAGETYPE_JPEG, IMAGETYPE_PNG);
if(!in_array($fileType, $allowed))
{
// don't overwrite an existing file
$i = 0;
$parts = pathinfo($file);
while(file_exists($folder . $name))
{
$i++;
$name = $Newname."-".$i.".".$parts["extension"];
}
if(move_uploaded_file($file, $folder.$name))
{
// set good permissions for the file
chmod($name, 0644);
return 'Uploaded!';
}else{
return 'Server Error!';
}
}
}
}
}
}
As long as you don't use the FileInfo (http://www.php.net/manual/en/ref.fileinfo.php) extensions from php to check the mime type, your function is not secure at all (think later you'll want to upload pdf's, excels, etc).
Also, md5 over md5 does nothing than increasing the collision chances.
L.E: Something as simple as the following should do it:
function getExtensionToMimeTypeMapping() {
return array(
'ai'=>'application/postscript',
'aif'=>'audio/x-aiff',
'aifc'=>'audio/x-aiff',
'aiff'=>'audio/x-aiff',
'anx'=>'application/annodex',
'asc'=>'text/plain',
'au'=>'audio/basic',
'avi'=>'video/x-msvideo',
'axa'=>'audio/annodex',
'axv'=>'video/annodex',
'bcpio'=>'application/x-bcpio',
'bin'=>'application/octet-stream',
'bmp'=>'image/bmp',
'c'=>'text/plain',
'cc'=>'text/plain',
'ccad'=>'application/clariscad',
'cdf'=>'application/x-netcdf',
'class'=>'application/octet-stream',
'cpio'=>'application/x-cpio',
'cpt'=>'application/mac-compactpro',
'csh'=>'application/x-csh',
'css'=>'text/css',
'csv'=>'text/csv',
'dcr'=>'application/x-director',
'dir'=>'application/x-director',
'dms'=>'application/octet-stream',
'doc'=>'application/msword',
'drw'=>'application/drafting',
'dvi'=>'application/x-dvi',
'dwg'=>'application/acad',
'dxf'=>'application/dxf',
'dxr'=>'application/x-director',
'eps'=>'application/postscript',
'etx'=>'text/x-setext',
'exe'=>'application/octet-stream',
'ez'=>'application/andrew-inset',
'f'=>'text/plain',
'f90'=>'text/plain',
'flac'=>'audio/flac',
'fli'=>'video/x-fli',
'flv'=>'video/x-flv',
'gif'=>'image/gif',
'gtar'=>'application/x-gtar',
'gz'=>'application/x-gzip',
'h'=>'text/plain',
'hdf'=>'application/x-hdf',
'hh'=>'text/plain',
'hqx'=>'application/mac-binhex40',
'htm'=>'text/html',
'html'=>'text/html',
'ice'=>'x-conference/x-cooltalk',
'ief'=>'image/ief',
'iges'=>'model/iges',
'igs'=>'model/iges',
'ips'=>'application/x-ipscript',
'ipx'=>'application/x-ipix',
'jpe'=>'image/jpeg',
'jpeg'=>'image/jpeg',
'jpg'=>'image/jpeg',
'js'=>'application/x-javascript',
'kar'=>'audio/midi',
'latex'=>'application/x-latex',
'lha'=>'application/octet-stream',
'lsp'=>'application/x-lisp',
'lzh'=>'application/octet-stream',
'm'=>'text/plain',
'man'=>'application/x-troff-man',
'me'=>'application/x-troff-me',
'mesh'=>'model/mesh',
'mid'=>'audio/midi',
'midi'=>'audio/midi',
'mif'=>'application/vnd.mif',
'mime'=>'www/mime',
'mov'=>'video/quicktime',
'movie'=>'video/x-sgi-movie',
'mp2'=>'audio/mpeg',
'mp3'=>'audio/mpeg',
'mpe'=>'video/mpeg',
'mpeg'=>'video/mpeg',
'mpg'=>'video/mpeg',
'mpga'=>'audio/mpeg',
'ms'=>'application/x-troff-ms',
'msh'=>'model/mesh',
'nc'=>'application/x-netcdf',
'oga'=>'audio/ogg',
'ogg'=>'audio/ogg',
'ogv'=>'video/ogg',
'ogx'=>'application/ogg',
'oda'=>'application/oda',
'pbm'=>'image/x-portable-bitmap',
'pdb'=>'chemical/x-pdb',
'pdf'=>'application/pdf',
'pgm'=>'image/x-portable-graymap',
'pgn'=>'application/x-chess-pgn',
'png'=>'image/png',
'pnm'=>'image/x-portable-anymap',
'pot'=>'application/mspowerpoint',
'ppm'=>'image/x-portable-pixmap',
'pps'=>'application/mspowerpoint',
'ppt'=>'application/mspowerpoint',
'ppz'=>'application/mspowerpoint',
'pre'=>'application/x-freelance',
'prt'=>'application/pro_eng',
'ps'=>'application/postscript',
'qt'=>'video/quicktime',
'ra'=>'audio/x-realaudio',
'ram'=>'audio/x-pn-realaudio',
'ras'=>'image/cmu-raster',
'rgb'=>'image/x-rgb',
'rm'=>'audio/x-pn-realaudio',
'roff'=>'application/x-troff',
'rpm'=>'audio/x-pn-realaudio-plugin',
'rtf'=>'text/rtf',
'rtx'=>'text/richtext',
'scm'=>'application/x-lotusscreencam',
'set'=>'application/set',
'sgm'=>'text/sgml',
'sgml'=>'text/sgml',
'sh'=>'application/x-sh',
'shar'=>'application/x-shar',
'silo'=>'model/mesh',
'sit'=>'application/x-stuffit',
'skd'=>'application/x-koan',
'skm'=>'application/x-koan',
'skp'=>'application/x-koan',
'skt'=>'application/x-koan',
'smi'=>'application/smil',
'smil'=>'application/smil',
'snd'=>'audio/basic',
'sol'=>'application/solids',
'spl'=>'application/x-futuresplash',
'spx'=>'audio/ogg',
'src'=>'application/x-wais-source',
'step'=>'application/STEP',
'stl'=>'application/SLA',
'stp'=>'application/STEP',
'sv4cpio'=>'application/x-sv4cpio',
'sv4crc'=>'application/x-sv4crc',
'swf'=>'application/x-shockwave-flash',
't'=>'application/x-troff',
'tar'=>'application/x-tar',
'tcl'=>'application/x-tcl',
'tex'=>'application/x-tex',
'texi'=>'application/x-texinfo',
'texinfo'=>'application/x-texinfo',
'tif'=>'image/tiff',
'tiff'=>'image/tiff',
'tr'=>'application/x-troff',
'tsi'=>'audio/TSP-audio',
'tsp'=>'application/dsptype',
'tsv'=>'text/tab-separated-values',
'txt'=>'text/plain',
'unv'=>'application/i-deas',
'ustar'=>'application/x-ustar',
'vcd'=>'application/x-cdlink',
'vda'=>'application/vda',
'viv'=>'video/vnd.vivo',
'vivo'=>'video/vnd.vivo',
'vrml'=>'model/vrml',
'wav'=>'audio/x-wav',
'wrl'=>'model/vrml',
'xbm'=>'image/x-xbitmap',
'xlc'=>'application/vnd.ms-excel',
'xll'=>'application/vnd.ms-excel',
'xlm'=>'application/vnd.ms-excel',
'xls'=>'application/vnd.ms-excel',
'xlw'=>'application/vnd.ms-excel',
'xml'=>'application/xml',
'xpm'=>'image/x-xpixmap',
'xspf'=>'application/xspf+xml',
'xwd'=>'image/x-xwindowdump',
'xyz'=>'chemical/x-pdb',
'zip'=>'application/zip',
);
}
function getMimeType($filePath) {
if (!is_file($filePath)) {
return false;
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $filePath);
finfo_close($finfo);
return $mime;
}
function upload($filePath, $destinationDir = 'images', array $allowedMimes = array()) {
if (!is_file($filePath) || !is_dir($destinationDir)) {
return false;
}
if (!($mime = getMimeType($filePath))) {
return false;
}
if (!in_array($mime, $allowedMimes)) {
return false;
}
$ext = null;
$extMapping = getExtensionToMimeTypeMapping();
foreach ($extMapping as $extension => $mimeType) {
if ($mimeType == $mime) {
$ext = $extension;
break;
}
}
if (empty($ext)) {
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
}
if (empty($ext)) {
return false;
}
$fileName = md5(uniqid(rand(0, time()), true)) . '.' . $ext;
$newFilePath = $destinationDir.'/'.$fileName;
if(!rename($filePath, $newFilePath)) {
return false;
}
return $fileName;
}
// use it
if (isset($_FILES['something']['tmp_name'])) {
$file = $_FILES['something']['tmp_name'];
$storagePath = 'images'; // this is relative to this script, better use absolute path.
$allowedMimes = array('image/png', 'image/jpg', 'image/gif', 'image/pjpeg');
$fileName = upload($file, $storagePath, $allowedMimes);
if (!$fileName) {
exit ('Your file type is not allowed.');
} else {
// check if file is image, optional, in case you allow multiple types of files.
// $imageInfo = #getimagesize($storagePath.'/'.$fileName);
exit ("Your uploaded file is {$fileName} and can be found at {$storagePath}/{$fileName}");
}
}
Stop filtering it by mime type it is not safe!
Client can send different mime types with different file extensions. So, you need to check file extension.
edit:
I think I have been misunderstood, I wrote the answer to tell that checking mime type to determine file type is not a good way, the best way to determine the file type is checking file extension. So, I don't mean that checking file extension is enough. Either checking only file extension or mime type is not safe way.
What to do?
1-Check mime type
2-Check file extension
3- decode file name
4- check file content consistency (if possible)
5- regenerate file content (if possible)
I know that attackers can bypass first and second way by using "null byte hack" and "mime type bypass"
So, 3,4 and 5 is so important for security.
Can anybody tell me how I can solve the following issue:
include('php/resizeImage.php');
if($_POST['uploadlink']){
$url = $_POST['uploadlink'];
$urlImage = file_get_contents($url);
if ($_POST['filename']){
$filename = $_POST['filename'].".jpg";
} else {
$urlinfo = parse_url($url);
$filename = basename($urlinfo['path']);
}
$image = new ResizeImage();
$image->load($filename);
$image->resizeToWidth(300);
$image->save($filename);
file_put_contents("images/upload/".$filename, $urlImage);
}
After I have received the image data from file_get_contents from a URL I want to resize it through my resizeImage script which takes the filename of the image as a parameter.
Edit: ResizeImage function load and resizeToWidth:
function load($filename) {
$image_info = getimagesize($filename);
$this->image_type = $image_info[2];
if( $this->image_type == IMAGETYPE_JPEG ) {
$this->image = imagecreatefromjpeg($filename);
} elseif( $this->image_type == IMAGETYPE_GIF ) {
$this->image = imagecreatefromgif($filename);
} elseif( $this->image_type == IMAGETYPE_PNG ) {
$this->image = imagecreatefrompng($filename);
}
}
function resizeToWidth($width) {
$ratio = $width / $this->getWidth();
$height = $this->getheight() * $ratio;
$this->resize($width,$height);
}
I have no trouble doing it when the user selects a local image through input type='file'.
if (isset($_FILES["uploadedfile"])){
$ufilename = $_FILES["uploadedfile"]["name"];
$ufiletmpname = $_FILES["uploadedfile"]["tmp_name"];
$image = new ResizeImage();
$image->load($ufiletmpname);
$image->resizeToWidth(300);
$image->save($ufiletmpname);
}
Another problem:
I forward the name of the user to my script because I want to create an individual folder for each user, so they only can see their own uploaded images.
$admin = $_GET['admin'];
file_put_contents("images/upload/".$admin."/".$filename, $urlImage);
Why is this not working for me?
Thanks.
Easy.
Just alter the code of whatever yours ResizeImage class to make it able to operate the image binary contents in addition to filename.
Your second question is quite simple too.
Setup your PHP installation to make it display errors on the screen (for the development server, of course!) and you will see the exact answer to your question, "Why is this not working for me?".
error_reporting(E_ALL);
ini_set('display_errors',1);
usually helps.
(also be sure your code do not do any HTTP redirects which may conceal the error messages from you)
What's ResizeImage?
If it were me, I'd do:
$data = file_get_contents($name);
$image = imagecreatefromstring($data);
// resize the image
For your first question, the ResizeImage is your own class, or something you downloaded from the net. To help you, we need to see it.
For the second part, file_put_contents won't create directories to you, to do that, you need to use the mkdir function.
SOLVED MY OWN QUESTION! THANKS EVERYONE FOR THE HELP :)
Ok. I'm having trouble with the code below recognizing the upload FILE TYPE and running the correct function. I can upload PNG just fine and it will convert and resize like it should, but GIF and JPEG don't and just return a black image. If I remove the png code and try the others individually they work. I can't figure this out at the moment why when I combine them they won't work. It's like together they all use whatever function comes first, instead of going by the FILE TYPE
if ($width > $max_width){
$scale = $max_width/$width;
if ($_FILE['image']['type'] = "image/png"){
$uploaded = resizeImagePNG($large_image_location,$width,$height,$scale);
} elseif ($_FILE['image']['type'] = "image/gif"){
$uploaded = resizeImageGIF($large_image_location,$width,$height,$scale);
} elseif ($_FILE['image']['type'] = "image/jpeg" || $_FILE['image']['type'] = "image/pjpeg"){
$uploaded = resizeImageJPG($large_image_location,$width,$height,$scale);
}
session_start();
$_SESSION['image2resize'] = $large_image_location;
}else{
$scale = 1;
if ($_FILE['image']['type'] = "image/png"){
$uploaded = resizeImagePNG($large_image_location,$width,$height,$scale);
} elseif ($_FILE['image']['type'] = "image/gif"){
$uploaded = resizeImageGIF($large_image_location,$width,$height,$scale);
} elseif ($_FILE['image']['type'] = "image/jpeg" || $_FILE['image']['type'] = "image/pjpeg"){
$uploaded = resizeImageJPG($large_image_location,$width,$height,$scale);
}
session_start();
$_SESSION['image2resize'] = $large_image_location;
}
}
edit: combined with Pekka's method for the mime, and rewritten for clarity
You have an error in all your if/elseif comparations. You need to put double == instead of single =
You may use this code that should do the same, but in a cleaner and safer way
$info = getimagesize(($_FILE['image']['tmp_name']);
$mime = $info["mime"];
if ($width > $max_width){
$scale = $max_width/$width;
} else {
$scale = 1;
}
switch ($mime)
{
case "image/png":
$uploaded = resizeImagePNG($large_image_location,$width,$height,$scale);
break;
case "image/gif":
$uploaded = resizeImageGIF($large_image_location,$width,$height,$scale);
break;
case "image/jpeg":
$uploaded = resizeImageJPG($large_image_location,$width,$height,$scale);
break;
default:
// do a better handling of the error
die('image type not supported');
}
session_start();
$_SESSION['image2resize'] = $large_image_location;
Also, don't rely on $_FILE['image']['type'], as this value is sent by the browser and an attacker can forge it. Use the getimagesize() method for obtaining the filetype as Pekka suggested in his answer.
#Carlos answers your question.
As a side note, I wouldn't rely on the MIME type server by the user's browser at all, and use getimagesize() to detect the file type instead.
$info = getimagesize(($_FILE['image']['tmp_name']);
$mime = $info["mime"];
that is safer.
FIGURED IT OUT!!!!!!!! The verification was screwing things up. Instead of checking if it's not a image, I checked if it was and it started working. NEW verification -> if ($mime == 'image/gif' || $mime == 'image/jpeg' || $mime == 'image/pjpeg' || $mime == 'image/png' || $_FILES['image']['size'] < 3000000){ working code here } else { error code here } . Thanks for all the help!