Как я могу получить 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, в зависимости от того, что вы предпочитаете использовать.

Другие вопросы по тегам