Блог Артёма Агасиева

Telegram: @aagasiev

В Go завезли функции min и max

Ура, в release notes к go 1.21:

The new functions min and max compute the smallest (or largest, for max) value of a fixed number of given arguments.

Хвала богам, не прошло и 10 лет, как в Go завезли такие нужные функции — min и max.

Определяем расширение файла в Python

Потребовалось сейчас сделать быструю проверку того, что тип скачиваемого программой файла — картинка в формате webp, а если это не она, то не качать ее вовсе, ну или хотя бы не полностью.

Самый быстрый способ — сделать HEAD запрос и проверить content-type, например, с помощью питоновской библиотеки mimetypes:


import requests
import mimetypes

r = requests.head("https://.../image.webp")
file_ext = mimetypes.guess_extension(r.headers.get("content-type"), strict=False)

if file_ext == "webp":
    pass

Можно ли точно полагаться на ответ сервера? В целом, нет, ведь даже если этот заголовок с виду корректен, то в тело ответа сервер может запихнуть что угодно. Однако, можно отсеивать очевидно ненужные варианты типа text/plain или чего-то подобного.

А еще бывает так, что нужный файл отдается с не тем типом контента. Например, в моем случае нужное хранилище AWS S3 грешило тем, что отдавало файл картинки с заголовком binary/octet-stream.

А еще HEAD запрос может не поддерживаться веб-сервером.

И что делать-то? Будем использовать обертку над libmagic, библиотеку python-magic. Она позволит нам по небольшому чанку данных узнать mime тип получаемого контента.


import os
import magic
import requests
import mimetypes

local_filename = "/local/file/name.webp"

r = requests.get("https://.../image.webp", stream=True)
f = open(local_filename, "wb")

for chunk in r.iter_content(1024):
    mime = magic.from_buffer(chunk, mime=True)
    file_ext = mimetypes.guess_extension(mime, strict=False)

    if new_file_ext != "webp":
        f.close()
        os.remove(local_filename)
        break

    f.write(chunk)
else:
    f.close()
    # Тут уже что-то можем делать с полученным файлом.


Вроде, выглядит норм. Ну, еще обработку ошибок добавить бы не помешало, да.

Но вот с точки зрения работы с сетью есть некоторые сомнения, что в случае вызова break, requests реально считает только 1 кб данных. Беглое гугление скорее усугубило мои сомнения. Надо иметь это в виду и как-нибудь на досуге реально протестировать.

В общем, все.

P.S. Если mimetypes не смогла определить расширение файла webp по типу контента image/webp, то его стоит добавить вручную в один из файлов mime.types вашей системы. Например, для Ubuntu это файл /etc/mime.types, в который надо добавить строку:


image/webp    webp

Другие файлики, откуда mimetypes может загружать себе данные, прописаны в самой библиотеке в массиве knownfiles:


knownfiles = [
    "/etc/mime.types",
    "/etc/httpd/mime.types",                    # Mac OS X
    "/etc/httpd/conf/mime.types",               # Apache
    "/etc/apache/mime.types",                   # Apache 1
    "/etc/apache2/mime.types",                  # Apache 2
    "/usr/local/etc/httpd/conf/mime.types",
    "/usr/local/lib/netscape/mime.types",
    "/usr/local/etc/httpd/conf/mime.types",     # Apache 1.2
    "/usr/local/etc/mime.types",                # Apache 1.3
]

Ошибки энкодера go-webp

Оказывается, единственная работающая WebP энкодер библиотека go-webp не умеет писать в bytes.Buffer через bufio.NewWriter() картинки маленького размера.

То есть, у меня кропы пользовательских аватарок разрешением меньше 200х200 пикселей стабильно получались весом в ноль байт. Это разрешение может немного меняться, в зависимости от картинки.

Однако, происходит это только в Lossy энкодере, Lossless работает нормально. Но кому он нужен, с его гигантскими файлами.

Выход, внезапно, прост и банален: делать надо 1 в 1 как в официальном примере, то бишь, писать все сразу в файл. Тогда картинка получится нормального размера и все будет хорошо.

Почему так происходит разбираться, если честно, лень. Если кто-то докопается до истины, отпишитесь пожалуйста.

Gmail и точка

Забавный факт, который мало кто учитывает при написании кода регистрации юзеров: точки в почтовом адресе Gmail не имеют значения.

То есть, с точки зрения гуглопочты, эти адреса равны между собой:

  • johnsmith@gmail.com
  • john.smith@gmail.com
  • jo.hn.sm.ith@gmail.com
  • j.o.h.n.s.m.i.t.h@gmail.com

Этим хаком часто пользуются спамеры, которые могут зарегистрировать на одну почту кучу аккаунтов и атаковать с них ваш сервис.

Так что, для внутреннего поиска/индексации все точки в gmail.com адресах лучше удалить. Главное, самому пользователю показывать почту в том виде, в котором он ее ввел, а то испугается и побежит в саппорт.

Ранее Ctrl + ↓