Django文件存储 默认存储系统解析
Django默认使用的文件存储系统'django.core.files.storage.FileSystemStorage'是一个本地存储系统,由settings中的DEFAULT_FILE_STORAGE值确定。
classFileSystemStorage(location=None,base_url=None,file_permissions_mode=None,directory_permissions_mode=None)
FileSystemStorage类继承自Storage类,location是存储文件的绝对路径,默认值是settings中的MEDIA_ROOT值,base_url默认值是settings中的MEDIA_URL值。
当定义location参数时,可以无视MEDIA_ROOT值来存储文件:
fromdjango.dbimportmodels fromdjango.core.files.storageimportFileSystemStorage fs=FileSystemStorage(location='/media/photos') classCar(models.Model): ... photo=models.ImageField(storage=fs)
这样文件会存储在/media/photos文件夹。
可以直接使用Django的文件存储系统来存储文件:
>>>fromdjango.core.files.storageimportdefault_storage
>>>fromdjango.core.files.baseimportContentFile
>>>path=default_storage.save('/path/to/file',ContentFile('newcontent'))
>>>path
'/path/to/file'
>>>default_storage.size(path)
11
>>>default_storage.open(path).read()
'newcontent'
>>>default_storage.delete(path)
>>>default_storage.exists(path)
False
可以从FileSystemStorage类的_save方法看下上传文件是怎么存储的:
def_save(self,name,content):
full_path=self.path(name)
#Createanyintermediatedirectoriesthatdonotexist.
#Notethatthereisaracebetweenos.path.existsandos.makedirs:
#ifos.makedirsfailswithEEXIST,thedirectorywascreated
#concurrently,andwecancontinuenormally.Refs#16082.
directory=os.path.dirname(full_path)
ifnotos.path.exists(directory):
try:
ifself.directory_permissions_modeisnotNone:
#os.makedirsappliestheglobalumask,soweresetit,
#forconsistencywithfile_permissions_modebehavior.
old_umask=os.umask(0)
try:
os.makedirs(directory,self.directory_permissions_mode)
finally:
os.umask(old_umask)
else:
os.makedirs(directory)
exceptOSErrorase:
ife.errno!=errno.EEXIST:
raise
ifnotos.path.isdir(directory):
raiseIOError("%sexistsandisnotadirectory."%directory)
#There'sapotentialraceconditionbetweenget_available_nameand
#savingthefile;it'spossiblethattwothreadsmightreturnthe
#samename,atwhichpointallsortsoffunhappens.Soweneedto
#trytocreatethefile,butifitalreadyexistswehavetogoback
#toget_available_name()andtryagain.
whileTrue:
try:
#Thisfilehasafilepaththatwecanmove.
ifhasattr(content,'temporary_file_path'):
file_move_safe(content.temporary_file_path(),full_path)
#Thisisanormaluploadedfilethatwecanstream.
else:
#Thisfunbinaryflagincantationmakesos.openthrowan
#OSErrorifthefilealreadyexistsbeforeweopenit.
flags=(os.O_WRONLY|os.O_CREAT|os.O_EXCL|
getattr(os,'O_BINARY',0))
#Thecurrentumaskvalueismaskedoutbyos.open!
fd=os.open(full_path,flags,0o666)
_file=None
try:
locks.lock(fd,locks.LOCK_EX)
forchunkincontent.chunks():
if_fileisNone:
mode='wb'ifisinstance(chunk,bytes)else'wt'
_file=os.fdopen(fd,mode)
_file.write(chunk)
finally:
locks.unlock(fd)
if_fileisnotNone:
_file.close()
else:
os.close(fd)
exceptOSErrorase:
ife.errno==errno.EEXIST:
#Ooops,thefileexists.Weneedanewfilename.
name=self.get_available_name(name)
full_path=self.path(name)
else:
raise
else:
#OK,thefilesaveworked.Breakoutoftheloop.
break
ifself.file_permissions_modeisnotNone:
os.chmod(full_path,self.file_permissions_mode)
#Storefilenameswithforwardslashes,evenonWindows.
returnforce_text(name.replace('\\','/'))
方法中可以看出,先判断文件存储的目录是否存在,如果不存在,使用os.mkdirs()依次创建目录。
根据directory_permissions_mode参数来确定创建的目录的权限,应该为(0777&~umask)。
然后使用os.open()创建文件,flags参数为(os.O_WRONLY|os.O_CREAT|os.O_EXCL|getattr(os,'O_BINARY',0)),
这样当文件已存在时,则报EEXIST异常,使用get_available_name()方法重新确定文件的名字。
mode为0o666,权限为(0666&~umask)。
content为FILE对象,如一切正常,使用FILE.chunks()依次将内容写入文件。
最后,根据file_permissions_mode参数,修改创建文件的权限。