Как использовать магию для проверки типа файла в чистом методе Django? - PullRequest
5 голосов
/ 27 декабря 2011

Я написал класс формы электронной почты в Django с FileField. Я хочу проверить загруженный файл на предмет его типа, проверив его mimetype. Впоследствии я хочу ограничить типы файлов PDF-файлами, Word и открытыми офисными документами.

Для этого я установил python-magic и хотел бы проверить типы файлов в соответствии со спецификациями для python-magic:

mime = magic.Magic(mime=True)
file_mime_type = mime.from_file('address/of/file.txt')

Однако недавно загруженным файлам не хватает адресов на моем сервере. Я также не знаю ни одного метода объекта MIME, похожего на «from_file_content», который проверяет тип MIME с учетом содержимого файла.

Как эффективно использовать магию для проверки типов загружаемых файлов в формах Django?

Ответы [ 5 ]

4 голосов
/ 28 декабря 2011

Стэн описал хороший вариант с буфером. К сожалению, недостатком этого метода является чтение файла в память. Другой вариант - использовать временно сохраненный файл:

import tempfile
import magic
with tempfile.NamedTemporaryFile() as tmp:
    for chunk in form.cleaned_data['file'].chunks():
        tmp.write(chunk)
    print(magic.from_file(tmp.name, mime=True))

Также вы можете проверить размер файла:

if form.cleaned_data['file'].size < ...:
    print(magic.from_buffer(form.cleaned_data['file'].read()))
else:
    # store to disk (the code above)

Дополнительно

Возможность использования имени для открытия файла во второй раз, когда именованный временный файл все еще открыт, зависит от платформы (его можно использовать в Unix; в Windows NT или более поздней версии это невозможно).

Так что вы можете обращаться с этим как так :

import os
tmp = tempfile.NamedTemporaryFile(delete=False)
try:
    for chunk in form.cleaned_data['file'].chunks():
        tmp.write(chunk)
    print(magic.from_file(tmp.name, mime=True))
finally:
    os.unlink(tmp.name)
    tmp.close()

Кроме того, вы можете захотеть seek(0) после read():

if hasattr(f, 'seek') and callable(f.seek):
    f.seek(0)

Где хранятся загруженные данные

4 голосов
/ 27 декабря 2011

Почему бы не попробовать что-то подобное на ваш взгляд:

m = magic.Magic()
m.from_buffer(request.FILES['my_file_field'].read())

Или используйте request.FILES вместо form.cleaned_data, если django.forms.Form действительно не вариант.

3 голосов
/ 17 октября 2014
mime = magic.Magic(mime=True)

attachment = form.cleaned_data['attachment']

if hasattr(attachment, 'temporary_file_path'):
    # file is temporary on the disk, so we can get full path of it.
    mime_type = mime.from_file(attachment.temporary_file_path())
else:
    # file is on the memory
    mime_type = mime.from_buffer(attachment.read())

Кроме того, вы можете seek(0) после read():

if hasattr(f, 'seek') and callable(f.seek):
    f.seek(0)

Пример из Код Джанго . Выполняется для полей изображения во время проверки.

0 голосов
/ 06 апреля 2019

Если вы обрабатываете загрузку файла и заинтересованы только в изображениях, Джанго установит для вас content_type (точнее для себя?):

from django.forms import ModelForm
from django.core.files import File
from django.db import models
class MyPhoto(models.Model):
    photo = models.ImageField(upload_to=photo_upload_to, max_length=1000)
class MyForm(ModelForm):
    class Meta:
        model = MyPhoto
        fields = ['photo']
photo = MyPhoto.objects.first()
photo = File(open('1.jpeg', 'rb'))
form = MyForm(files={'photo': photo})
if form.is_valid():
    print(form.instance.photo.file.content_type)

Это не зависит от типа контента, предоставленного пользователем. Но django.db.models.fields.files.FieldFile.file является недокументированным свойство .

На самом деле, изначально content_type устанавливается из запроса , но когда форма проверяется, значение обновлено .

Что касается non-images, делает request.FILES['name'].read() мне кажется вполне приемлемым. Во-первых, это то, что делает Django . Во-вторых, файлы размером более 2,5 Мб по умолчанию хранятся на диске . Итак, позвольте мне указать вам на другой ответ здесь.


Для любопытных, вот трассировка стека, которая приводит к обновлению content_type

django.forms.forms.BaseForm.is_valid: self.errors
django.forms.forms.BaseForm.errors: self.full_clean ()
django.forms.forms.BaseForm.full_clean: self._clean_fields ()
django.forms.forms.BaseForm._clean_fiels: field.clean ()
django.forms.fields.FileField.clean: super (). clean ()
django.forms.fields.Field.clean: self.to_python ()
django.forms.fields.ImageField. to_python

0 голосов
/ 07 февраля 2018

Вы можете использовать пакет django-safe-filefield , чтобы проверить, что расширение загруженного файла соответствует его MIME-типу.

from safe_filefield.forms import SafeFileField

class MyForm(forms.Form):

    attachment = SafeFileField(
        allowed_extensions=('xls', 'xlsx', 'csv')
    )
...