2013-04-08 12 views
19

Kullanıcı tarafından yüklenen dosyaları tutan bir FileField modelim var. Yerden tasarruf etmek istediğim için, kopyalardan kaçınmak istiyorum.Django yüklemeleri: Yüklenen kopyaları sil, varolan dosyayı kullan (md5 tabanlı onay)

elde etmek istiyorum Ne:

  1. hesaplayın yüklenen dosyalar MD5 sağlama onun md5sum dayalı dosya adıyla
  2. Mağaza dosyasını
  3. ise Bu ada sahip bir dosya zaten var (yeni dosya bir yinelenen), Yüklenen dosyayı atmak ve mevcut dosyasını kullanmak yerine

ve zaten çalışıyor, ancak nasıl Yüklenen yinelenen unutmak istiyorum ve bunun yerine varolan dosyayı kullanmak? Ben ve üzerine yazmak değil varolan bir dosyayı tutmak istiyorum

Not (çoğunlukla aynı modifiye zaman tutmak için - daha iyi yedekleme için).

Notlar:

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) 

Herhangi hel:

  • Django 1.5
  • kullanıyorum yükleme işleyicisi django.core.files.uploadhandler.TemporaryFileUploadHandler

Kod olduğu p takdir edilir!

+0

Ne kadar trafik almayı planlıyorsunuz? Küçük bir proje veya özel bir proje ise, Amazon S3 veya Rackspace Cloudfiles veya 0,50 $/ay için başka bir ucuz dosya deposu için 0,50 $/ay'ın üzerinde bir çare yapabilirsiniz. –

cevap

25

teşekkürler, ben custom storage class yazma anahtarı olduğunu anlamaya başardı ve beklenenden daha kolaydı.

  • Sadece zaten varsa dosyayı yazmak için süper sınıf _save yöntemini çağırarak ihmal ve sadece adını döndürür.
  • aynı isimde bir dosya zaten bu bunu yapmanın doğru yolu ise bilmiyorum

mevcut ise dosya adına eklenmiş numaraları almamak için, get_available_name üzerine, ancak şimdiye kadar gayet iyi çalışıyor.

Umarım bu yararlıdır!

İşte tam örnek kod:

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) 
+0

gerçekten güzel kod: yolunda h [0: 1], h [1: 2] kullanımı nedir? – zinking

+0

Oh, sadece farklı dizinlere dağıtmak için/0/0/-/f/f/Tüm dosyaları tek bir dosyada saklamak istemedim. – phoibos

+0

bu hala veritabanında yeni bir pk ama aynı dosya adıyla bir giriş oluşturacak - bunu nasıl hallettin? – MJP

6

AFAIK kaydetme/silme yöntemlerini kullanarak bunu kolayca uygulayamazsınız coz dosyaları oldukça özel olarak ele alınır.

Ama bunun gibi bir şey deneyebilirsiniz.

Birincisi, benim basit md5 dosyası hash fonksiyonu:

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

Sonraki simple_upload_to olan media_file_name fonksiyon sizinki gibi smth olduğunu. Böyle kullanmalı? Elbette

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()) 

, yol nesil mantık çeşitli olabilir yani sadece bir örnek.

Ve en önemli kısmı:

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) 

bu özel depolama kaydetmeden önce dosyayı siler ve ardından aynı adla yeni bir tane kaydeder görebileceğiniz gibi. Bu yüzden, eğer silme (ve dolayısıyla güncelleme) dosyaları önemli değilse, mantığınızı burada uygulayabilirsiniz.

Daha depolarda yaklaşık ou burada bulabilirsiniz: Altus cevaba https://docs.djangoproject.com/en/1.5/ref/files/storage/

+0

Teşekkürler! Cevabınız beni doğru yola getirdi. – phoibos

0

Bu cevap beni dosya zaten yükleniyor var olma durumunda bir istisna yükseltmek isteyen sorunu çözmek yardımcı oldu. Bu sürüm, yükleme konumundaki aynı ada sahip bir dosya zaten varsa bir istisna ortaya çıkarır.

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) 
2

Aynı sorunu yaşadım ve bu SO sorusunu buldum. SHA1 sağlamalarının söz konusu ise

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

Bir çekme isteği MD5 karma eklemek için düşünüyorum: Bu ben web arandı ve istediğini tam olarak yapmak dikişleri aşağıdaki Python paketini buldum çok nadir bir şey olduğu için destek harika bir fikir olurdu.