I'm trying to find the mime type of an image. PHP has the function getimagesize
but that only takes a filename, whereas I have an image "resource" instead - i.e. an image created from imagecreatefromstring
.
I found the functions imagesx
and imagesy
which return the width/height from a resource but I can't find any function that tell me the mime type from a resource. Anyone know of a way to do this?
Note: Due to a weird server set up, we can't read/write files from the server normally, only through an FTP layer (which is where I read the image data from).
If you've got access to the binary data of the image (as the use of imagecreatefromstring() suggests), you can detect the file-type "manually":
function image_file_type_from_binary($binary) {
if (
!preg_match(
'/\A(?:(\xff\xd8\xff)|(GIF8[79]a)|(\x89PNG\x0d\x0a)|(BM)|(\x49\x49(?:\x2a\x00|\x00\x4a))|(FORM.{4}ILBM))/',
$binary, $hits
)
) {
return 'application/octet-stream';
}
static $type = array (
1 => 'image/jpeg',
2 => 'image/gif',
3 => 'image/png',
4 => 'image/x-windows-bmp',
5 => 'image/tiff',
6 => 'image/x-ilbm',
);
return $type[count($hits) - 1];
}
Abusing a stream wrapper gets a bit more complicated. At least if we don't want to fiddle with global variables.
// getimagesize() from string
class GisFromString {
const proto_default = 'gisfromstring';
protected static $proto = null;
protected static $imgdata = null;
static function getImageSize($imgdata) {
if (null === self::$proto) {
self::register();
}
self::$imgdata = $imgdata;
// note: @ suppresses "Read error!" notices if $imgdata isn't valid
return @getimagesize(self::$proto . '://');
}
static function getMimeType($imgdata) {
return is_array($gis = self::getImageSize($imgdata))
? $gis['mime']
: $gis;
}
// streamwrapper helper:
const unregister = null;
// register|unregister wrapper for the given protocol|scheme
// return registered protocol or null
static function register(
$proto = self::proto_default // protocol or scheme
) {
if (self::unregister === $proto) { // unregister if possible
if (null === self::$proto) {
return null;
}
if (!stream_wrapper_unregister(self::$proto)) {
return null;
}
$return = self::$proto;
self::$proto = null;
return $return;
}
if (!preg_match('/\A([a-zA-Z][a-zA-Z0-9.+\-]*)(:([\/\x5c]{0,3}))?/', $proto, $h)) {
throw new Exception(
sprintf('could not register invalid scheme or protocol name "%s" as streamwrapper', $proto)
);
}
if (!stream_wrapper_register($proto = $h[1], __CLASS__)) {
throw new Exception(
sprintf('protocol "%s" already registered as streamwrapper', $proto)
);
}
return self::$proto = $proto;
}
// streamwrapper API:
function stream_open($path, $mode) {
$this->str = (string) self::$imgdata;
$this->fsize = strlen($this->str);
$this->fpos = 0;
return true;
}
function stream_close() {
self::$imgdata = null;
}
function stream_read($num_bytes) {
if (!is_numeric($num_bytes) || $num_bytes < 1) {
return false;
}
/* uncomment this if needed
if ($this->fpos + $num_bytes > 65540 * 4) {
// prevent getimagesize() from scanning the whole file
// 65_540 is the maximum possible bytesize of a JPEG segment
return false;
}
*/
if ($this->fpos + $num_bytes > $this->fsize) {
$num_bytes = $this->fsize - $this->fpos;
}
$read = substr($this->str, $this->fpos, $num_bytes);
$this->fpos += strlen($read);
return $read;
}
function stream_eof() {
return $this->fpos >= $this->fsize;
}
function stream_tell() {
return $this->fpos;
}
function stream_seek($off, $whence = SEEK_SET) {
if (SEEK_CUR === $whence) {
$off = $this->fpos + $off;
}
elseif (SEEK_END === $whence) {
$off = $this->fsize + $off;
}
if ($off < 0 || $off > $this->fsize) {
return false;
}
$this->fpos = $off;
return true;
}
}
// usage:
//$imgdata = file_get_contents('path/lenna.jpg');
// if the default protocol is already registered
//GisFromString::register('other');
var_dump(GisFromString::getImageSize($imgdata));
echo GisFromString::getMimeType($imgdata);