Как я могу получить MIME-тип байта, хранящегося в PostgreSQL
Как мы можем получить байты mime-типа, хранящиеся в базе данных Postgres?
2 ответа
Я знаю, что ответ немного опоздал, но помог бы другим.
По предложению @ craig-ringer я реализовал магию мимов в plpythonu
, поэтому я могу использовать эту функцию в триггере INSERT/UPDATE.
На сервере базы данных установите plpythonu
для вашей версии postgresql (например, через apt-get
или же yum
). Нет необходимости перезапускать / перезагружать базу данных, так что это может быть легко сделано в производстве.
Затем установите модуль 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's BYTEA
типа карты для питона string
тип. Для питона 3 он отображается на питонов bytes
тип.
После того, как вы создали функцию, вы можете использовать функцию 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-тип определяется по требованию.
Нет необходимости говорить, что выполнение функции для каждой строки в каждом запросе является огромным ударом по производительности, поэтому определение типа 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
Чтобы получить лучшие результаты, вы должны, при наличии, также попробовать обнаружение, основанное на имени файла /-extension, поскольку только 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
Нет никакого способа точно знать тип MIME, кроме как присвоить его другому полю, когда вы сохраняете bytea
на первом месте.
Предполагая, что у вас есть байтовые массивы неизвестного типа, которые представляют файлы или другие достаточно полные объекты, которые могут иметь тип MIME, вы можете использовать инструменты угадывания MIME-типа так же, как и для файлов. Эти инструменты далеки от совершенства, но хорошо работают с обычными типами файлов с регулярными и предсказуемыми заголовками.
В PostgreSQL такого встроенного инструмента нет, но PostgreSQL поддерживает вызов процедурных языков, таких как PL/Python и PL/Perl. Эти языки имеют инструменты угадывания MIME-типов.
Поэтому я предлагаю написать функцию-обертку в PL / Perl или PL/Python, которая использует соответствующую библиотеку угадывания типов MIME для проверки bytea
аргумент и вернуть предполагаемый тип MIME. Детали выбора и реализации библиотеки оставлены читателю в качестве упражнения; Я бы начал с руководства PostgreSQL по PL / Perl или PL/Python, в зависимости от того, что вы предпочитаете использовать.