我们如何获得存储在 Postgres 数据库中的 mime 类型的 bytea?
2 回答
我知道答案有点晚,但会有所帮助。
正如@craig-ringer 所建议的,我已经在 中实现了 mime-magic plpythonu
,所以我可以在我的 INSERT/UPDATE-Trigger 中使用该函数。
在数据库服务器上安装plpythonu
您的 postgresql 版本(例如通过apt-get
或yum
)。无需重新启动/重新加载数据库,因此可以在生产环境中轻松完成。
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
除了首先存储时将其分配给另一个字段之外,没有办法明确知道 MIME 类型bytea
。
假设您有表示文件或其他可能具有 MIME 类型的相当完整的对象的未知类型的字节数组,您可以像在文件上一样使用 MIME 类型猜测工具。这些工具远非完美,但适用于具有常规和可预测标题的常见行为良好的文件类型。
PostgreSQL 没有内置这样的工具,但 PostgreSQL 支持调用过程语言,如 PL/Python 和 PL/Perl。这些语言确实有 MIME 类型猜测工具。
所以我建议在 PL/Perl 或 PL/Python 中编写一个包装函数,它使用适当的 MIME 类型猜测库来探测bytea
参数并返回猜测的 MIME 类型。图书馆选择和实施的细节留给读者练习;我将从 PL/Perl 或 PL/Python 的 PostgreSQL 手册开始,无论您喜欢使用哪个。