从 PHP/GD 中的资源获取图像 mimetype?

发布于 2024-08-20 05:16:30 字数 304 浏览 4 评论 0原文

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

我发现函数 imagesximagesy 返回资源的宽度/高度,但我找不到任何函数告诉我资源的 mime 类型。有人知道有办法做到这一点吗?

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

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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓦然回首 2024-08-27 05:16:30

如果您可以访问图像的二进制数据(正如使用 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);

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);
魄砕の薆 2024-08-27 05:16:30

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

不久前问过同样的问题相同的结果。

唯一的方法是在图像被 imagecreatefromstring 编辑之前捕获图像,并以某种方式从中捕获大小信息。

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

getimagesize() 无法从变量中读取的事实是众所周知的,并且令人遗憾: http:// /bugs.php.net/bug.php?id=44239

那里的人提到了一个漂亮的解决方法:注册一个新的流包装器,允许对变量进行文件操作

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

An image created using imagecreatefromstring has no MIME type any more, it is decoded from its native format and stored in GD's internal format.

The same question was asked a while back with the same result.

The only way to go is to catch the image before it gets imagecreatefromstringed, and somehow catch the size information from it.

You say that you can't do file read/write operations on your system, so just writing out the file is out of the question.

The fact that getimagesize() can't read from variables is known and lamented: http://bugs.php.net/bug.php?id=44239

The guy there mentions a nifty workaround: Registering a new stream wrapper that allows file operations on variables.

Is this an option on your server setup?

嘿嘿嘿 2024-08-27 05:16:30

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

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

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

I know this is pretty old, but just in case someone come across this post like I did...

A better option it's been released from PHP 5.4.0:
getimagesizefromstring

This new function is exactly the same of getimagesize but allows you to retreive the information from a stream.

你爱我像她 2024-08-27 05:16:30

您可以使用 PHP fileinfo 函数。

$image_buffer = SomeFunctionToGetStringBufferFromGD();

$fileinfo = finfo_open();

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

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

You could use the PHP fileinfo functions.

$image_buffer = SomeFunctionToGetStringBufferFromGD();

$fileinfo = finfo_open();

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

It uses the magic numbers (same as the unix file command) to identify the file type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文