addons-server/apps/amo/storage_utils.py

92 строки
3.0 KiB
Python

"""
Utilities for working with the Django Storage API.
A lot of these methods assume the use of a storage backend that does not
require leading directories to exist. The default Django file system storage
*will* sometimes require leading directories to exist.
"""
from django.core.files.storage import default_storage
from django.utils.encoding import smart_str
DEFAULT_CHUNK_SIZE = 64 * 2 ** 10 # 64kB
def walk_storage(path, topdown=True, onerror=None, followlinks=False,
storage=default_storage):
"""
Generate the file names in a stored directory tree by walking the tree
top-down.
For each directory in the tree rooted at the directory top (including top
itself), it yields a 3-tuple (dirpath, dirnames, filenames).
This is intended for use with an implementation of the Django storage API.
You can specify something other than the default storage instance with
the storage keyword argument.
"""
if not topdown:
raise NotImplementedError
if onerror:
raise NotImplementedError
roots = [path]
while len(roots):
new_roots = []
for root in roots:
dirs, files = storage.listdir(root)
files = [smart_str(f) for f in files]
dirs = [smart_str(d) for d in dirs]
yield root, dirs, files
for dn in dirs:
new_roots.append('%s/%s' % (root, dn))
roots[:] = new_roots
def copy_stored_file(src_path, dest_path, storage=default_storage,
chunk_size=DEFAULT_CHUNK_SIZE):
"""
Copy one storage path to another storage path.
Each path will be managed by the same storage implementation.
"""
if src_path == dest_path:
return
with storage.open(src_path, 'rb') as src:
with storage.open(dest_path, 'wb') as dest:
done = False
while not done:
chunk = src.read(chunk_size)
if chunk != '':
dest.write(chunk)
else:
done = True
def move_stored_file(src_path, dest_path, storage=default_storage,
chunk_size=DEFAULT_CHUNK_SIZE):
"""
Move a storage path to another storage path.
The source file will be copied to the new path then deleted.
This attempts to be compatible with a wide range of storage backends
rather than attempt to be optimized for each individual one.
"""
copy_stored_file(src_path, dest_path, storage=storage,
chunk_size=chunk_size)
storage.delete(src_path)
def rm_stored_dir(dir_path, storage=default_storage):
"""
Removes a stored directory and all files stored beneath that path.
"""
empty_dirs = []
# Delete all files first then all empty directories.
for root, dirs, files in walk_storage(dir_path):
for fn in files:
storage.delete('%s/%s' % (root, fn))
empty_dirs.insert(0, root)
empty_dirs.append(dir_path)
for dn in empty_dirs:
storage.delete(dn)