1

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

4

2 回答 2

1

我知道答案有点晚,但会有所帮助。

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

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

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

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,它映射到 pythonsbytes类型。

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

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 类型并保存在文件内容旁边:

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 被检测为文本/纯文本)。也可以使用魔术的选项,例如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

于 2016-02-17T17:20:48.950 回答
1

除了首先存储时将其分配给另一个字段之外,没有办法明确知道 MIME 类型bytea

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

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

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

于 2015-07-16T12:17:56.313 回答