2010-09-06 30 views
39

我怎麼能限制FileField僅在一個優雅的方式,服務器端接受某種類型的文件(視頻,音頻,PDF等)的?只接受的FileField某個文件類型,服務器端

+1

爲了讓打開的對話框來限制文件對某些類型的客戶端,[見這個問題(HTTP: //stackoverflow.com/a/40847649/247696)。 – Flimm 2016-11-28 15:24:40

回答

44

一個非常簡單的方法是使用自定義驗證器。

在您的應用程序的validators.py

def validate_file_extension(value): 
    import os 
    from django.core.exceptions import ValidationError 
    ext = os.path.splitext(value.name)[1] # [0] returns path+filename 
    valid_extensions = ['.pdf', '.doc', '.docx', '.jpg', '.png', '.xlsx', '.xls'] 
    if not ext.lower() in valid_extensions: 
     raise ValidationError(u'Unsupported file extension.') 

然後在您的models.py

from .validators import validate_file_extension 

...和使用驗證你的表單字段:

class Document(models.Model): 
    file = models.FileField(upload_to="documents/%Y/%m/%d", validators=[validate_file_extension]) 

參見: How to limit file types on file uploads for ModelForms with FileFields?

+1

這應該被接受爲答案。非常感謝! – vabada 2016-06-28 10:33:23

+2

@dabad只使用擴展名對文件驗證不好,這類答案會造成安全漏洞;請看到這些答案的人也檢查與不同格式和驗證器相關的CVE,以pillow/PIL爲例:O – 2016-08-24 06:10:50

10

有一個Django snippet做這個:

import os 

from django import forms 

class ExtFileField(forms.FileField): 
    """ 
    Same as forms.FileField, but you can specify a file extension whitelist. 

    >>> from django.core.files.uploadedfile import SimpleUploadedFile 
    >>> 
    >>> t = ExtFileField(ext_whitelist=(".pdf", ".txt")) 
    >>> 
    >>> t.clean(SimpleUploadedFile('filename.pdf', 'Some File Content')) 
    >>> t.clean(SimpleUploadedFile('filename.txt', 'Some File Content')) 
    >>> 
    >>> t.clean(SimpleUploadedFile('filename.exe', 'Some File Content')) 
    Traceback (most recent call last): 
    ... 
    ValidationError: [u'Not allowed filetype!'] 
    """ 
    def __init__(self, *args, **kwargs): 
     ext_whitelist = kwargs.pop("ext_whitelist") 
     self.ext_whitelist = [i.lower() for i in ext_whitelist] 

     super(ExtFileField, self).__init__(*args, **kwargs) 

    def clean(self, *args, **kwargs): 
     data = super(ExtFileField, self).clean(*args, **kwargs) 
     filename = data.name 
     ext = os.path.splitext(filename)[1] 
     ext = ext.lower() 
     if ext not in self.ext_whitelist: 
      raise forms.ValidationError("Not allowed filetype!") 

#------------------------------------------------------------------------- 

if __name__ == "__main__": 
    import doctest, datetime 
    doctest.testmod() 
+2

這是一個基於擴展名的過濾器,它根本不可靠。上傳完成後,我正在考慮分析文件。 – maroxe 2010-09-08 15:48:52

+0

@maroxe您是否找到解決方案?我只是在確定是否有audiofiles文件時遇到同樣的問題。 – marue 2011-02-17 13:42:59

+3

嘗試此鏈接:http://nemesisdesign.net/blog/coding/django-filefield-content-type-size-validation/ – PhoebeB 2011-02-22 15:40:43

7

第一。在應用程序中創建一個名爲formatChecker.py的文件,其中您擁有要接受某種文件類型的FileField的模型。

這是您的formatChecker.py:

from django.db.models import FileField 
from django.forms import forms 
from django.template.defaultfilters import filesizeformat 
from django.utils.translation import ugettext_lazy as _ 

class ContentTypeRestrictedFileField(FileField): 
    """ 
    Same as FileField, but you can specify: 
     * content_types - list containing allowed content_types. Example: ['application/pdf', 'image/jpeg'] 
     * max_upload_size - a number indicating the maximum file size allowed for upload. 
      2.5MB - 2621440 
      5MB - 5242880 
      10MB - 10485760 
      20MB - 20971520 
      50MB - 5242880 
      100MB 104857600 
      250MB - 214958080 
      500MB - 429916160 
""" 
def __init__(self, *args, **kwargs): 
    self.content_types = kwargs.pop("content_types") 
    self.max_upload_size = kwargs.pop("max_upload_size") 

    super(ContentTypeRestrictedFileField, self).__init__(*args, **kwargs) 

def clean(self, *args, **kwargs):   
    data = super(ContentTypeRestrictedFileField, self).clean(*args, **kwargs) 

    file = data.file 
    try: 
     content_type = file.content_type 
     if content_type in self.content_types: 
      if file._size > self.max_upload_size: 
       raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s') % (filesizeformat(self.max_upload_size), filesizeformat(file._size))) 
     else: 
      raise forms.ValidationError(_('Filetype not supported.')) 
    except AttributeError: 
     pass   

    return data 

二。在你的models.py,補充一點:

from formatChecker import ContentTypeRestrictedFileField 

然後,而不是使用 '的FileField',用這個 'ContentTypeRestrictedFileField'。

例子:

class Stuff(models.Model): 
    title = models.CharField(max_length=245) 
    handout = ContentTypeRestrictedFileField(upload_to='uploads/', content_types=['video/x-msvideo', 'application/pdf', 'video/mp4', 'audio/mpeg', ],max_upload_size=5242880,blank=True, null=True) 

這些都是你擁有的東西,當你想只接受的FileField某些文件類型。

+0

內容類型很容易被騙 – 2013-05-07 09:57:31

+3

超級調用之前添加此行:self.widget = ClearableFileInput(attrs = {'接受':','。join(self.content_types)}) 將只允許在模式窗口中選擇接受的內容類型。 – laffuste 2013-06-18 05:58:14

+0

@laffuste有了你的評論,這似乎不適用於Mac(即任何文件都是可選的)。雖然沒有在Windows下測試過。 – Erve1879 2013-07-12 08:13:49

2

我認爲你最適合使用Dominic Rodger在他的回答中指定的ExtFileFieldpython-magic Daniel Quinn提到的最好的方法。如果某個人足夠聰明,至少可以改變擴展名,那麼你可以用標題來捕捉它們。

+0

既然你已經確定這是最好的方法,爲什麼不張貼一些代碼來向我們展示呢? – Thismatters 2017-12-11 15:50:04

1

此外,我會擴大這個類與一些額外的行爲。

class ContentTypeRestrictedFileField(forms.FileField): 
    ... 
    widget = None 
    ... 
    def __init__(self, *args, **kwargs): 
     ... 
     self.widget = forms.ClearableFileInput(attrs={'accept':kwargs.pop('accept', None)}) 
     super(ContentTypeRestrictedFileField, self).__init__(*args, **kwargs) 

當我們創建實例與PARAM接受=「PDF,.TXT」,在彈出的與文件結構作爲默認,我們將看到通過擴展名的文件。

1

您可以在設置中定義接受的MIME類型的列表,然後確定其使用Python魔法來檢測MIME類型,並提出ValidationError如果MIME類型是不被接受的驗證。在文件表單字段上設置驗證器。

唯一的問題是,有時的mime類型是application /八位字節流,這可能對應於不同的文件格式。你們有人解決了這個問題嗎?

11

版本1.11中的Django爲模型字段新增了FileExtensionValidator,文檔位於:https://docs.djangoproject.com/en/dev/ref/validators/#fileextensionvalidator

如何驗證文件擴展名的例子:

from django.core.validators import FileExtensionValidator 
from django.db import models 

class MyModel(models.Model): 
    pdf_file= models.FileField(upload_to='foo/', validators=[FileExtensionValidator(allowed_extensions=['pdf'])]) 

注意,此方法是並不安全。引用自Django文檔:

不要依賴文件擴展名的確認來確定文件的 類型。無論文件包含哪些數據 ,文件都可以重命名爲具有任何擴展名。

還有新的validate_image_file_extensionhttps://docs.djangoproject.com/en/dev/ref/validators/#validate-image-file-extension)用於驗證圖像擴展名(使用Pillow)。

2

您可以使用下面的表單中的

file = forms.FileField(widget=forms.FileInput(attrs={'accept':'application/pdf'})) 
0

有幾個人使用python-magic,以驗證該文件實際上是你期望接收的類型建議限制文件類型。這可以併入到在接受的答案建議validator

import os 
import magic 
from django.core.exceptions import ValidationError 

def validate_is_pdf(file): 
    valid_mime_types = ['application/pdf'] 
    file_mime_type = magic.from_buffer(file.read(1024), mime=True) 
    if file_mime_type not in valid_mime_types: 
     raise ValidationError('Unsupported file type.') 
    valid_file_extensions = ['.pdf'] 
    ext = os.path.splitext(file.name)[1] 
    if ext.lower() not in valid_file_extensions: 
     raise ValidationError('Unacceptable file extension.') 

該示例僅驗證一個pdf,但任何數量的MIME類型和文件擴展名的可被添加到陣列。

假設你保存在上面validators.py你可以加入到這個模型中,像這樣:

from myapp.validators import validate_is_pdf 

class PdfFile(models.Model): 
    file = models.FileField(upload_to='pdfs/', validators=(validate_is_pdf,)) 
相關問題