how form files example python django django-models django-file-upload

python - form - Cargas de Django: descarte los duplicados cargados, use el archivo existente(verificación basada en md5)



upload image django example (4)

AFAIK, no puede implementar esto fácilmente utilizando los métodos de guardar / eliminar porque los archivos se manejan de manera bastante específica.

Pero podrías intentar algo así.

Primero, mi simple función hash de archivos md5:

def md5_for_file(chunks): md5 = hashlib.md5() for data in chunks: md5.update(data) return md5.hexdigest()

La siguiente simple_upload_to is es algo así como la función media_file_name de tuyo. Deberías usarlo así:

def simple_upload_to(field_name, path=''files''): def upload_to(instance, filename): name = md5_for_file(getattr(instance, field_name).chunks()) dot_pos = filename.rfind(''.'') ext = filename[dot_pos:][:10].lower() if dot_pos > -1 else ''.unknown'' name += ext return os.path.join(path, name[:2], name) return upload_to class Media(models.Model): # see info about storage below orig_file = models.FileField(upload_to=simple_upload_to(''orig_file''), storage=MyCustomStorage())

Por supuesto, es solo un ejemplo, por lo que la lógica de generación de rutas podría ser diferente.

Y la parte más importante:

from django.core.files.storage import FileSystemStorage class MyCustomStorage(FileSystemStorage): def get_available_name(self, name): return name def _save(self, name, content): if self.exists(name): self.delete(name) return super(MyCustomStorage, self)._save(name, content)

Como puede ver, este almacenamiento personalizado elimina el archivo antes de guardar y luego guarda uno nuevo con el mismo nombre. Por lo tanto, aquí puede implementar su lógica si NO eliminar (y, por lo tanto, actualizar) los archivos es importante.

Puede encontrar más información sobre los almacenes aquí: https://docs.djangoproject.com/en/1.5/ref/files/storage/

Tengo un modelo con un FileField , que contiene los archivos cargados por el usuario. Como quiero ahorrar espacio, me gustaría evitar duplicados.

Lo que me gustaría lograr:

  1. Calcula los archivos subidos md5 checksum
  2. Almacene el archivo con el nombre del archivo basado en su md5sum
  3. Si ya existe un archivo con ese nombre (el nuevo archivo es un duplicado ), descarte el archivo cargado y use el archivo existente en su lugar.

1 y 2 ya están funcionando, pero ¿cómo podría olvidarme de un duplicado cargado y utilizar el archivo existente en su lugar?

Tenga en cuenta que me gustaría mantener el archivo existente y no sobrescribirlo (principalmente para mantener el tiempo modificado igual - mejor para la copia de seguridad).

Notas:

  • Estoy usando Django 1.5
  • El controlador de carga es django.core.files.uploadhandler.TemporaryFileUploadHandler

Código:

def media_file_name(instance, filename): h = instance.md5sum basename, ext = os.path.splitext(filename) return os.path.join(''mediafiles'', h[0:1], h[1:2], h + ext.lower()) class Media(models.Model): orig_file = models.FileField(upload_to=media_file_name) md5sum = models.CharField(max_length=36) ... def save(self, *args, **kwargs): if not self.pk: # file is new md5 = hashlib.md5() for chunk in self.orig_file.chunks(): md5.update(chunk) self.md5sum = md5.hexdigest() super(Media, self).save(*args, **kwargs)

Cualquier ayuda es apreciada!


Esta respuesta me ayudó a resolver el problema en el que quería provocar una excepción si el archivo que se estaba cargando ya existía. Esta versión genera una excepción si ya existe un archivo con el mismo nombre en la ubicación de carga.

from django.core.files.storage import FileSystemStorage class FailOnDuplicateFileSystemStorage(FileSystemStorage): def get_available_name(self, name): return name def _save(self, name, content): if self.exists(name): raise ValidationError(''File already exists: %s'' % name) return super( FailOnDuplicateFileSystemStorage, self)._save(name, content)


Gracias a alTus answer, pude averiguar que escribir una clase de almacenamiento personalizada es la clave, y fue más fácil de lo esperado.

  • Simplemente _save llamar a las superclases _save método para escribir el archivo si ya está allí y simplemente devuelvo el nombre.
  • get_available_name para evitar que se get_available_name números al nombre del archivo si ya existe un archivo con el mismo nombre

No sé si esta es la forma correcta de hacerlo, pero hasta ahora funciona bien.

Espero que esto sea útil!

Aquí está el código de ejemplo completo:

import hashlib import os from django.core.files.storage import FileSystemStorage from django.db import models class MediaFileSystemStorage(FileSystemStorage): def get_available_name(self, name, max_length=None): if max_length and len(name) > max_length: raise(Exception("name''s length is greater than max_length")) return name def _save(self, name, content): if self.exists(name): # if the file exists, do not call the superclasses _save method return name # if the file is new, DO call it return super(MediaFileSystemStorage, self)._save(name, content) def media_file_name(instance, filename): h = instance.md5sum basename, ext = os.path.splitext(filename) return os.path.join(''mediafiles'', h[0:1], h[1:2], h + ext.lower()) class Media(models.Model): # use the custom storage class fo the FileField orig_file = models.FileField( upload_to=media_file_name, storage=MediaFileSystemStorage()) md5sum = models.CharField(max_length=36) # ... def save(self, *args, **kwargs): if not self.pk: # file is new md5 = hashlib.md5() for chunk in self.orig_file.chunks(): md5.update(chunk) self.md5sum = md5.hexdigest() super(Media, self).save(*args, **kwargs)


Tuve el mismo problema y encontré esta pregunta SO. Como esto no es nada raro, busqué en la web y encontré el siguiente paquete de Python, que parece hacer exactamente lo que quieres:

https://pypi.python.org/pypi/django-hashedfilenamestorage

Si los hashes SHA1 están fuera de cuestión, creo que una solicitud de extracción para agregar el soporte de hashing MD5 sería una gran idea.