2013-04-08 11 views
19

私はFileFieldというモデルを持っています。私はスペースを節約したいので、私は重複を避けたいと思います。Djangoアップロード:アップロードされた重複を破棄し、既存のファイルを使用します(md5ベースのチェック)

私が達成したいのですがどのような

  1. 計算アップロードされたファイルMD5チェックサム
  2. ストアそのmd5sumを
  3. に基づいてファイル名を持つファイルの場合その名前のファイルがすでに存在しています(新しいファイルのと重複しています)、アップロードされたファイルを破棄して、既存のファイルを使用する代わりに、

とが既に取り組んでいるが、はどのように私は、アップロードされた重複を忘れるだろうし、代わりに、既存のファイルを使用できますか?私は( - バックアップのためのより良い主に同じ更新時刻を保つために)それを上書きしませ既存のファイルを維持し、したい

注意。

注:

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) 

どれHEL:

  • 私は、アップロード・ハンドラがdjango.core.files.uploadhandler.TemporaryFileUploadHandler

コードでDjangoの1.5

  • を使用していますpは高く評価されています!

  • +0

    どのくらいのトラフィックを取得する予定ですか?小規模なプロジェクトやプライベートプロジェクトの場合は、Amazon S3、Rackspace Cloudfiles、または他の安価なファイルストアの月額$ 0.50以上をフォークすることができます。 –

    答えて

    25

    おかげで、私はcustom storage classを書くことが鍵であり、そして予想以上に簡単だったことを把握することができました。

    • 私はちょうどそれがすでにあると私は名前だけを返す場合は、ファイルを書き込むことが_saveメソッドスーパークラスを呼び出す省略します。
    • 私は同じ名前のファイルが既に、これはそれを行うの適切な方法であれば、私は知らない

    を既存されている場合、ファイル名に付加番号を避けるために、get_available_nameを上書きしますが、これまでのところうまく動作します。

    希望すると便利です。

    はここで完全なサンプルコードです:

    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

    本当にいいコードです:h [0:1]、h [1:2]をパスに使うのは何ですか? – zinking

    +0

    これはちょうど異なるディレクトリ/ 0/0/-/f/f /に配布するためのものです。私はすべてのファイルを1つに保存したくありませんでした。 – phoibos

    +0

    これは、新しいpkでデータベースに同じエントリを作成しますが、同じファイル名です。これはどのように処理しましたか? – MJP

    6

    AFAIKあなたはsave/deleteメソッドを使用して簡単にこれを実装することはできません。cozファイルは非常に具体的に扱われます。

    しかし、あなたはそのようなスムーズさを試すことができます。

    まず、私の単純なMD5ファイルのハッシュ関数:

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

    simple_upload_toでは、あなたのmedia_file_name機能のようななめらかです。 あなたはそのようにそれを使用する必要があります。もちろん

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

    、パス生成ロジックは、様々なことができるよう、それは単なる例です。

    そして、最も重要な部分:

    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) 
    

    このカスタムストレージを保存する前にファイルを削除してから、同じ名前で新しいものを保存し見ることができるように。 ファイルを削除しない(そして更新する)ことが重要である場合、ここであなたのロジックを実装することができます。ストレージのOUについて

    詳細はここに見つけることができます:アルタスの答えにhttps://docs.djangoproject.com/en/1.5/ref/files/storage/

    +0

    ありがとう!あなたの答えは私を正しい道に導きました。 – phoibos

    0

    この答えは私がアップロードされたファイルが既に存在していた場合、私は例外を発生したかった問題を解決する助けました。このバージョンでは、同じ名前のファイルがアップロード場所に既に存在する場合、例外が発生します。

    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

    私は同じ問題を抱えていたので、この問題が見つかりました。これは私がウェブを検索し、正確に何をしたい行うには縫い目次のPythonパッケージを見つけ、あまりにも珍しいものではありませんよう:

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

    SHA1ハッシュは問題外であれば、私はMD5ハッシュを追加するためのプル要求を考えますサポートは素晴らしい考えです。

    関連する問題