Core.Files 模块

Django 的核心模块之一,封装文件操作,提供对文件的上传、下载、删除、查看大小等功能。

提供的功能

  1. 存储后端解耦,支持多种存储后端,使得在不同存储后端中的文件数据都能以相同的方式操作
  2. 文件上传、下载、删除、查看大小等功能

支持的文件存储后端

  • FileSystemStorage:基于本地文件系统的存储后端,适用于开发环境和小型项目。
  • MemoryStorage:基于内存的存储后端,适用于开发环境,小型项目,处理一些临时文件。
第三方存储后端库
  • django-storages:支持 S3, LibCloud, Azure, DigitalOcean, Google Cloud Storage, Backblaze B2, SFTP, FTP, Dropbox, Oracle Cloud 等云存储服务。
  • django-oss-storage: 支持阿里云 OSS 对象存储服务, 可能已经失效但仍可以 fork 修改。
  • django-qiniu-storage: 支持七牛云对象存储服务, 可能已经失效但仍可以 fork 修改。

支持的文件类型

  • File:普通文件,可以是任何类型的文件,如图片、视频、文档等。
  • ImageFile:图片文件,继承自 File,提供图片处理的功能,也没什么功能就多了,height,width 两个方法,需要安装 Pillow 库。
  • UploadedFile: 抽象类,提供文件上传的基本功能,主要是满足 form-data 上传的文件。
  • TemporaryUploadedFile(继承自 UploadedFile): 存放在临时文件中
  • InMemoryUploadedFile(继承自 UploadedFile): 存储在内存中

可以学习的地方

  1. 自定义文件存储后端
class Storage:# 文件存储后端无关的接口,可以提供默认实现def open(self, name, mode="rb"): ...def save(self, name, content, max_length=None): ...def get_valid_name(self, name): ...def get_alternative_name(self, file_root, file_ext): ...def get_available_name(self, name, max_length=None): ...def generate_filename(self, filename): ...def path(self, name): ...# 与文件存储后端相关的接口,需要子类实现def _open(self, name, mode="rb"):raise NotImplementedError("subclasses of Storage must provide an _open() method")def delete(self, name):"""Delete the specified file from the storage system."""raise NotImplementedError("subclasses of Storage must provide a delete() method")def exists(self, name):"""Return True if a file referenced by the given name already exists in thestorage system, or False if the name is available for a new file."""raise NotImplementedError("subclasses of Storage must provide an exists() method")def listdir(self, path):"""List the contents of the specified path. Return a 2-tuple of lists:the first item being directories, the second item being files."""raise NotImplementedError("subclasses of Storage must provide a listdir() method")def size(self, name):"""Return the total size, in bytes, of the file specified by name."""raise NotImplementedError("subclasses of Storage must provide a size() method")def url(self, name):"""Return an absolute URL where the file's contents can be accesseddirectly by a web browser."""raise NotImplementedError("subclasses of Storage must provide a url() method")def get_accessed_time(self, name):"""Return the last accessed time (as a datetime) of the file specified byname. The datetime will be timezone-aware if USE_TZ=True."""raise NotImplementedError("subclasses of Storage must provide a get_accessed_time() method")def get_created_time(self, name):"""Return the creation time (as a datetime) of the file specified by name.The datetime will be timezone-aware if USE_TZ=True."""raise NotImplementedError("subclasses of Storage must provide a get_created_time() method")def get_modified_time(self, name):"""Return the last modified time (as a datetime) of the file specified byname. The datetime will be timezone-aware if USE_TZ=True."""raise NotImplementedError("subclasses of Storage must provide a get_modified_time() method")
  1. 文件读写锁
def _fd(f):"""Get a filedescriptor from something which could be a file or an fd."""return f.fileno() if hasattr(f, "fileno") else fif os.name == "nt":import msvcrtfrom ctypes import (POINTER,Structure,Union,WinDLL,byref,c_int64,c_ulong,c_void_p,sizeof,)from ctypes.wintypes import BOOL, DWORD, HANDLELOCK_SH = 0# the defaultLOCK_NB = 0x1# LOCKFILE_FAIL_IMMEDIATELYLOCK_EX = 0x2# LOCKFILE_EXCLUSIVE_LOCK# --- Adapted from the pyserial project ---# detect size of ULONG_PTRif sizeof(c_ulong) != sizeof(c_void_p):ULONG_PTR = c_int64else:ULONG_PTR = c_ulongPVOID = c_void_p# --- Union inside Structure by stackoverflow:3480240 ---class _OFFSET(Structure):_fields_ = [("Offset", DWORD), ("OffsetHigh", DWORD)]class _OFFSET_UNION(Union):_anonymous_ = ["_offset"]_fields_ = [("_offset", _OFFSET), ("Pointer", PVOID)]class OVERLAPPED(Structure):_anonymous_ = ["_offset_union"]_fields_ = [("Internal", ULONG_PTR),("InternalHigh", ULONG_PTR),("_offset_union", _OFFSET_UNION),("hEvent", HANDLE),]LPOVERLAPPED = POINTER(OVERLAPPED)# --- Define function prototypes for extra safety ---kernel32 = WinDLL("kernel32")LockFileEx = kernel32.LockFileExLockFileEx.restype = BOOLLockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, DWORD, LPOVERLAPPED]UnlockFileEx = kernel32.UnlockFileExUnlockFileEx.restype = BOOLUnlockFileEx.argtypes = [HANDLE, DWORD, DWORD, DWORD, LPOVERLAPPED]def lock(f, flags):hfile = msvcrt.get_osfhandle(_fd(f))overlapped = OVERLAPPED()ret = LockFileEx(hfile, flags, 0, 0, 0xFFFF0000, byref(overlapped))return bool(ret)def unlock(f):hfile = msvcrt.get_osfhandle(_fd(f))overlapped = OVERLAPPED()ret = UnlockFileEx(hfile, 0, 0, 0xFFFF0000, byref(overlapped))return bool(ret)else:try:import fcntlLOCK_SH = fcntl.LOCK_SH# shared lockLOCK_NB = fcntl.LOCK_NB# non-blockingLOCK_EX = fcntl.LOCK_EXexcept (ImportError, AttributeError):# File locking is not supported.LOCK_EX = LOCK_SH = LOCK_NB = 0# Dummy functions that don't do anything.def lock(f, flags):# File is not lockedreturn Falsedef unlock(f):# File is unlockedreturn Trueelse:def lock(f, flags):try:fcntl.flock(_fd(f), flags)return Trueexcept BlockingIOError:return Falsedef unlock(f):fcntl.flock(_fd(f), fcntl.LOCK_UN)return True

总结

对于 web 服务端开发,文件操作是日常。但是文件的用途有很多,处置方式也很多。例如不同的文件类型放置在不同的存储后端,以及文件的访问控制、时效性也可以通过拓展存储后端来实现。
利用 django 这种分离文件操作和存储后端的框架,在开发中可以很好的解决大部分文件类需求。