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参数,修改创建文件的权限。