如何获取PostgreSQL中存储的bytea的MIME类型

时间:2015-07-16 04:30:07

标签: postgresql bytearray postgresql-9.2 postgresql-9.3 bytea

我们如何获得mime-type的bytea 存储在Postgres数据库中?

2 个答案:

答案 0 :(得分:1)

在您首先存储bytea时,除了将其分配给其他字段外,无法明确地知道MIME类型。

假设您有未知类型的字节数组来表示可能具有MIME类型的文件或其他合理完整的对象,您可以像使用文件一样使用MIME类型的猜测工具。这些工具远非完美,但适用于具有常规和可预测标头的常见性能良好的文件类型。

PostgreSQL没有内置这样的工具,但PostgreSQL支持调用PL / Python和PL / Perl等过程语言。这些语言具有MIME类型猜测工具。

所以我建议在PL / Perl或PL / Python中编写一个包装函数,它使用适当的MIME类型猜测库来探测bytea参数并返回猜测的MIME类型。图书馆选择和实施的细节留给读者练习;我将从PL / Perl或PL / Python上的PostgreSQL手册开始,无论您喜欢使用哪种。

答案 1 :(得分:0)

我知道答案有点晚,但会帮助其他人。

根据@ craig-ringer的建议,我在plpythonu中实现了mime-magic,所以我可以在INSERT / UPDATE-Trigger中使用该函数。

在数据库服务器上为postgresql版本安装plpythonu(例如,通过apt-getyum)。不需要重新启动/重新加载数据库,因此可以在生产中轻松完成。

然后在数据库服务器上安装python模块magic

pip install python-magic

之后在您的数据库中创建plpythonu语言:

CREATE LANGUAGE plpythonu;

现在您可以在python中编写数据库函数(甚至是导入python模块):

CREATE OR REPLACE FUNCTION mimemagic(data bytea) RETURNS TEXT AS $$
    import magic
    return magic.from_buffer(data, mime=True)
$$ LANGUAGE plpythonu;

如果使用python2,postgresql的BYTEA类型将映射到python的string类型。 对于python 3,它映射到pythons bytes类型。

创建函数后,可以像任何其他postgresql函数一样在任何语句或触发器函数中使用plpythonu函数。 要测试我们的函数,我们可以创建一个如下所示的表并插入一些文件:

CREATE TABLE public.files (
  file_id BIGINT PRIMARY KEY NOT NULL DEFAULT nextval('files_file_id_seq'::regclass),
  name TEXT NOT NULL,
  extension TEXT,
  content BYTEA NOT NULL,
  mime_type TEXT
);

示例查询:

SELECT name, extension, mime_type, mimemagic(content) FROM files;

结果:

             name                 | extension | mime_type |    mimemagic     
----------------------------------+-----------+-----------+-----
 10868_170915_1M                  | pdf       |           | application/pdf
 30567_160415_1M                  | pdf       |           | application/pdf
 Diode-SCS                        | dxf       |           | text/plain
 Config                           | zip       |           | application/zip
 btn-default-medium-focus-corners | gif       |           | image/gif
 _loadmask                        | scss      |           | text/plain
 mr                               | json      |           | text/plain
 10549_160415_2M                  | pdf       |           | application/pdf
 disconnect                       | png       |           | image/png

正如您所看到的,表中没有保存mime_type,但是根据需要检测到mime-type。

不需要说每个查询的每一行都执行一个函数是一个巨大的性能损失,所以mime类型检测应该在INSERT / UPDATE和缓存上完成:

示例触发器:

CREATE OR REPLACE FUNCTION files_trigger()
  RETURNS TRIGGER AS $$
BEGIN
  IF (TG_OP = 'INSERT') OR (TG_OP = 'UPDATE' AND new.content IS DISTINCT FROM old.content)
  THEN
    new.mime_type := mimemagic(new.content);
  END IF;
  RETURN new;
END
$$ LANGUAGE plpgsql;

CREATE TRIGGER files_insertupdate_trigger BEFORE INSERT OR UPDATE ON files
    FOR EACH ROW EXECUTE PROCEDURE files_trigger();

现在检测到Mime Type一次,并保存在filecontent旁边:

SELECT name, extension, mime_type FROM files;

结果:

                   name                   | extension |          mime_type           
------------------------------------------+-----------+------------------------------
 teamviewer_10.0.35509_amd64              | deb       | application/x-debian-package
 BDFE999CCC39110229563FA8C8583E239F6BDBA1 | log       | text/plain
 daccr-Download                           | exe       | application/x-dosexec

为了获得更好的结果,您应该在可用的情况下尝试基于文件名/扩展的检测,因为某些类型的mimemagic单独失败(json-File被检测为text / plain)。 也可以使用像uncompress=True这样的魔术选项,它可以提供更多可用的结果:

>>> import magic
>>> import mimetypes
>>> filename='verybig.json.gz'
>>> m=magic.Magic(mime=True, uncompress=False)
>>> m.from_file(filename)
'application/gzip'
>>> m=magic.Magic(mime=True, uncompress=True)
>>> m.from_file(filename)
'text/plain'
>>> mimetypes.guess_type(filename)
('application/json', 'gzip')
>>> 

plpythonu的文档可以在这里找到:http://www.postgresql.org/docs/9.5/static/plpython.html

python的魔术模块的代码和文档可以在这里找到:https://github.com/ahupp/python-magic