with simple form example django django-models django-uploads

simple - Django: Valida el tipo de archivo subido



upload image django example (4)

Tengo una aplicación que permite a las personas cargar archivos, representados como UploadedFiles . Sin embargo, quiero asegurarme de que los usuarios solo carguen archivos xml. Sé que puedo hacer esto usando magic , pero no sé dónde poner esta comprobación. No puedo ponerla en la función de clean ya que el archivo aún no se ha cargado cuando se ejecuta la clean , por lo que puedo decir.

Aquí está el modelo de UploadedFile :

class UploadedFile(models.Model): """This represents a file that has been uploaded to the server.""" STATE_UPLOADED = 0 STATE_ANNOTATED = 1 STATE_PROCESSING = 2 STATE_PROCESSED = 4 STATES = ( (STATE_UPLOADED, "Uploaded"), (STATE_ANNOTATED, "Annotated"), (STATE_PROCESSING, "Processing"), (STATE_PROCESSED, "Processed"), ) status = models.SmallIntegerField(choices=STATES, default=0, blank=True, null=True) file = models.FileField(upload_to=settings.XML_ROOT) project = models.ForeignKey(Project) def __unicode__(self): return self.file.name def name(self): return os.path.basename(self.file.name) def save(self, *args, **kwargs): if not self.status: self.status = self.STATE_UPLOADED super(UploadedFile, self).save(*args, **kwargs) def delete(self, *args, **kwargs): os.remove(self.file.path) self.file.delete(False) super(UploadedFile, self).delete(*args, **kwargs) def get_absolute_url(self): return u''/upload/projects/%d'' % self.id def clean(self): if not "XML" in magic.from_file(self.file.url): raise ValidationError(u''Not an xml file.'') class UploadedFileForm(forms.ModelForm): class Meta: model = UploadedFile exclude = (''project'',)


Creo que lo que desea hacer es limpiar el archivo cargado en los métodos Form.clean_your_field_name_here() Django: los datos están disponibles en su sistema para entonces si se enviaron como una solicitud HTTP POST normal.

Además, si considera que es ineficiente, explore las opciones de los distintos backends de carga de archivos Django y cómo realizar el procesamiento de transmisión.

Si necesita considerar la seguridad del sistema cuando se trata de cargas

  • Asegúrate de que el archivo cargado tenga la extensión correcta

  • Asegúrese de que el mimetype coincida con la extensión del archivo

En el caso de que esté preocupado por la carga de archivos de exploits del usuario (por atacar contra su sitio)

  • Reescriba todo el contenido del archivo guardado para deshacerse de una posible carga adicional (explotar) (por lo que no puede incrustar HTML en XML que el navegador interpretaría como un archivo HTML de origen de sitio al descargar)

  • Asegúrese de usar el encabezado de disposición de contenido en la descarga

Más información aquí: http://opensourcehacker.com/2013/07/31/secure-user-uploads-and-exploiting-served-user-content/

A continuación se muestra mi ejemplo de cómo desinfecto las imágenes cargadas:

class Example(models.Model): image = models.ImageField(upload_to=filename_gen("participant-images/"), blank=True, null=True) class Example(forms.ModelForm): def clean_image(self): """ Clean the uploaded image attachemnt. """ image = self.cleaned_data.get(''image'', False) utils.ensure_safe_user_image(image) return image def ensure_safe_user_image(image): """ Perform various checks to sanitize user uploaded image data. Checks that image was valid header, then :param: InMemoryUploadedFile instance (Django form field value) :raise: ValidationError in the case the image content has issues """ if not image: return assert isinstance(image, InMemoryUploadedFile), "Image rewrite has been only tested on in-memory upload backend" # Make sure the image is not too big, so that PIL trashes the server if image: if image._size > 4*1024*1024: raise ValidationError("Image file too large - the limit is 4 megabytes") # Then do header peak what the image claims image.file.seek(0) mime = magic.from_buffer(image.file.getvalue(), mime=True) if mime not in ("image/png", "image/jpeg"): raise ValidationError("Image is not valid. Please upload a JPEG or PNG image.") doc_type = mime.split("/")[-1].upper() # Read data from cStringIO instance image.file.seek(0) pil_image = Image.open(image.file) # Rewrite the image contents in the memory # (bails out with exception on bad data) buf = StringIO() pil_image.thumbnail((2048, 2048), Image.ANTIALIAS) pil_image.save(buf, doc_type) image.file = buf # Make sure the image has valid extension (can''t upload .htm image) extension = unicode(doc_type.lower()) if not image.name.endswith(u".%s" % extension): image.name = image.name + u"." + extension


Desde django 1.11, también puede usar FileExtensionValidator.

from django.core.validators import FileExtensionValidator class UploadedFile(models.Model): file = models.FileField(upload_to=settings.XML_ROOT, validators=[FileExtensionValidator(allowed_extensions=[''xml''])])

Tenga en cuenta que esto debe usarse en un FileField y no funcionará en un CharField (por ejemplo), ya que el validador valida en value.name.

ref: https://docs.djangoproject.com/en/dev/ref/validators/#fileextensionvalidator


Para la posteridad: la solución es usar el método de read y pasarlo a magic.from_buffer .

class UploadedFileForm(ModelForm): def clean_file(self): file = self.cleaned_data.get("file", False) filetype = magic.from_buffer(file.read()) if not "XML" in filetype: raise ValidationError("File is not XML.") return file class Meta: model = models.UploadedFile exclude = (''project'',)


Validar archivos es un problema regular, por lo que me gustaría usar un validador:

from django.utils.deconstruct import deconstructible from django.template.defaultfilters import filesizeformat import magic @deconstructible class FileValidator(object): error_messages = { ''max_size'': ("Ensure this file size is not greater than %(max_size)s." " Your file size is %(size)s."), ''min_size'': ("Ensure this file size is not less than %(min_size)s. " "Your file size is %(size)s."), ''content_type'': "Files of type %(content_type)s are not supported.", } def __init__(self, max_size=None, min_size=None, content_types=()): self.max_size = max_size self.min_size = min_size self.content_types = content_types def __call__(self, data): if self.max_size is not None and data.size > self.max_size: params = { ''max_size'': filesizeformat(self.max_size), ''size'': filesizeformat(data.size), } raise ValidationError(self.error_messages[''max_size''], ''max_size'', params) if self.min_size is not None and data.size < self.min_size: params = { ''min_size'': filesizeformat(self.mix_size), ''size'': filesizeformat(data.size) } raise ValidationError(self.error_messages[''min_size''], ''min_size'', params) if self.content_types: content_type = magic.from_buffer(data.read(), mime=True) if content_type not in self.content_types: params = { ''content_type'': content_type } raise ValidationError(self.error_messages[''content_type''], ''content_type'', params) def __eq__(self, other): return isinstance(other, FileValidator)

Luego, puede usar FileValidator en su model.FileField o forms.FileField siguiente manera:

validate_file = FileValidator(max_size=1024 * 100, content_types=(''application/xml'',)) file = models.FileField(upload_to=settings.XML_ROOT, validators=[validate_file])