【问题标题】:Get image mimetype from resource in PHP/GD?从 PHP/GD 中的资源获取图像 mimetype?
【发布时间】:2011-01-13 12:09:52
【问题描述】:

我正在尝试查找图像的 MIME 类型。 PHP 有函数getimagesize,但它只接受一个文件名,而我有一个图像“资源”——即从imagecreatefromstring 创建的图像。

我找到了函数imagesximagesy,它们从资源中返回宽度/高度,但我找不到任何函数可以告诉我资源中的mime 类型。有人知道这样做的方法吗?

注意:由于服务器设置奇怪,我们无法正常从服务器读取/写入文件,只能通过 FTP 层(我从中读取图像数据)。

【问题讨论】:

标签: php image ftp gd


【解决方案1】:

我知道这已经很老了,但以防万一有人像我一样看到这篇文章......

从 PHP 5.4.0 发布的一个更好的选择: getimagesizefromstring

这个新函数与 getimagesize 完全相同,但允许您从流中检索信息。

【讨论】:

    【解决方案2】:

    如果您可以访问图像的二进制数据(正如使用 imagecreatefromstring() 所建议的那样),您可以“手动”检测文件类型:

    
    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];
    }
    

    滥用流包装器变得有点复杂。 至少如果我们不想摆弄全局变量的话。

    
    // 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);
    

    【讨论】:

    • 函数image_file_type_from_binary 不是故障安全的。有时正在上传文件,但 mimetype 不是图像/。通过在结束正斜杠后添加“i”使 preg_match 不区分大小写。 (在上面的脚本中进行了更改,但必须等待批准)使用此功能是一个很好的功能,因为并非所有网络主机都允许您使用 fileinfo 或 exif 扩展...
    • 当然它不是故障安全的。它只检查图像数据开头的“魔术字节”。将“/i”附加到正则表达式是一个相当糟糕的主意。二进制文件没有“大小写意识”的概念。
    【解决方案3】:

    您可以使用 PHP 文件信息函数。

    $image_buffer = SomeFunctionToGetStringBufferFromGD();
    
    $fileinfo = finfo_open();
    
    $type = finfo_buffer($fileinfo, $image_buffer);
    

    它使用magic numbers(与unix文件命令相同)来识别文件类型。

    【讨论】:

    • 这很好用。唯一的缺点是它返回一个字符串,您必须进一步解析才能获得 mime 类型,例如。 PNG image, 28 x 18, 8-bit colormap, non-interlaced
    【解决方案4】:

    使用imagecreatefromstring 创建的图像不再具有 MIME 类型,它从其原始格式解码并以 GD 的内部格式存储。

    同样的问题是asked a while back,结果相同。

    唯一的方法是在图像得到imagecreatefromstringed 之前捕获它,并以某种方式从中捕获大小信息。

    你说你不能在你的系统上进行文件读/写操作,所以只写出文件是不可能的。

    getimagesize() 无法从变量中读取这一事实已为人所知并感叹:http://bugs.php.net/bug.php?id=44239

    那里的人提到了一个绝妙的解决方法:Registering a new stream wrapper that allows file operations on variables

    这是您服务器设置中的一个选项吗?

    【讨论】:

    • 对不起,就像我说的我不能正常读/写文件到服务器,所以不能使用getimagesize,因为没有文件名。编辑:伙计,别那么急躁,我正在写一个解释......;)
    • 不能只使用 GD 资源,抱歉。但是,如果您imagecreatefromstring,您在某个时候拥有原始数据,对吗?为什么不将其写入磁盘并执行getimagesize?我同意这很愚蠢,但它会完成这项工作。编辑:这就是我先评论然后投票的原因;)第二次编辑:啊,你不能写入磁盘。而且我认为您不能将ftp:// 包装器用于imagesize()?
    • @DisgruntledGoat,我添加了第二种可能性。
    • FWIW,我也遇到了这个问题。 PHP 5.3 目前不可用,所以getimagesizefromstring() 不是一个选项(FileInfo 也不是),我不想用VariableStream 包装器使实现复杂化。下一个最好的方法是使用data:// 包装器,它允许您创建getimagesize() 可以读取的内存中URI。它还通过仅编码二进制数据的小子字符串来优化请求。实现的要点在这里:gist.github.com/3751491
    猜你喜欢
    • 2010-12-30
    • 2013-07-18
    • 2023-03-11
    • 2014-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多