需求说明
项目中有上传文件的需求,同时又想限制上传文件的大小,对于超过100mb的文件拒绝上传
django的settings文件设置
...
MEDIA_URL="/media/" # 访问文件的url根路径
MEDIA_ROOT=os.path.join(BASE_DIR, "media") # 文件存储根目录,可自行修改要保存的位置
MAX_FILE_SIZE = 104857600 # 限制最大文件100MB
...
自定义文件存储系统
在django的应用目录下(或其他位置)新建文件 storage.py
,代码如下:
class FileTooLarge(Exception):
"""
限制上传文件大小异常,可单独创建exception.py文件保存该类
"""
class LiterDocStorage(FileSystemStorage):
# location和base_url均可自定义,定义后settings中的设置就会失效,不建议在这里修改
def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
# 初始化,location
super(LiterDocStorage, self).__init__(location, base_url)
def _save(self, name, content):
"""重写限制文件大小为100MB"""
if content.size > settings.MAX_FILE_SIZE:
raise FileTooLarge('文件应小于%sMB' % int((settings.MAX_FILE_SIZE / (1024 * 1024))))
return super(LiterDocStorage, self)._save(name, content)
注意这里对文件大小的判断是不能通过self.size(name)获取的,这个方法是在视图函数中获取文件对象后调用的,file.size属性调用的该方法
通过调试输出 content 的类型,发现它是一个 InMemoryUploadedFile
在 django.core.files.uploaderdfile
中,定位源代码如下:
class InMemoryUploadedFile(UploadedFile):
"""
A file uploaded into memory (i.e. stream-to-memory).
"""
def __init__(self, file, field_name, name, content_type, size, charset, content_type_extra=None):
super().__init__(file, name, content_type, size, charset, content_type_extra)
self.field_name = field_name
def open(self, mode=None):
self.file.seek(0)
return self
def chunks(self, chunk_size=None):
self.file.seek(0)
yield self.read()
def multiple_chunks(self, chunk_size=None):
# Since it's in memory, we'll never have multiple chunks.
return False
可以在__init__方法中看到有属性 size,因此通过 content.size 得到文件大小,单位是byte
models中的设置
class MyModel(models.Model):
upload = models.FileField(upload_to='uploads/', storage=LiterDocStorage()) # 这里指定文件存储类型是自定义类
视图函数中使用
file = request.FILES.get('file') # 前端发送的文件
try:
MyModel.objects.create(upload=file)
except FileTooLarge as e: # 限制文件上传大小为100MB
code = 1
msg = '文件上传失败,%s' % str(e)
参考:
FileField