Django目录上传获取子目录名
Django directory upload get sub-directory names
我正在编写一个 django 应用程序来上传带有表单的文件目录。
这是我使用的允许上传目录的表格:
class FileFieldForm(forms.Form):
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs=
{'multiple': True, 'webkitdirectory': True, 'directory': True}))
这是原始的 post 负载:
------WebKitFormBoundaryPbO3HkrKGbBwgD3sd1
Content-Disposition: form-data; name="csrfmiddlewaretoken"
F575Bgl4U9dzgwePPeSW2ISZKk5c3CnRoqFasdasD0Hep6nD0LnAAObXbF92SUa96NbO2
------WebKitFormBoundaryPbO3HkrKGbBwgDsd31
Content-Disposition: form-data; name="file_field";
filename="MainDir/SubDir1/1.jpg"
Content-Type: image/jpeg
------WebKitFormBoundaryPbOasd3HkrKGbBwgD31
Content-Disposition: form-data; name="file_field";
filename="MainDir/SubDir2/2.jpg"
Content-Type: image/jpeg
这是处理表单的视图:
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'upload.html'
success_url = 'upload'
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
pprint("Name of file is " + f._get_name() + ' ' + f.field_name, sys.stderr)
new_file = FileModel(file=f)
new_file.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
问题是 django 中的文件对象名称没有子目录名称。我假设中间件处理请求之一是从文件名中解析和删除子目录名称。有什么方法可以获取包含目录和子目录名称的原始文件名?
我相信这就是 Django 的实现方式。请参考Django's Upload Handler doc.
它有默认的上传处理程序 MemoryFileUploadHandler
和 TemporaryFileUploadHandler
。它们都使用 UploadedFile
来处理文件,并且它有一个函数 _set_name
,它采用文件的基本名称。
甚至有评论说为什么它采用基本名称:
def _set_name(self, name):
# Sanitize the file name so that it can't be dangerous.
if name is not None:
# Just use the basename of the file -- anything else is dangerous.
name = os.path.basename(name)
# File names longer than 255 characters can cause problems on older OSes.
if len(name) > 255:
name, ext = os.path.splitext(name)
ext = ext[:255]
name = name[:255 - len(ext)] + ext
self._name = name
但我认为您可以编写自己的上传处理程序,它不采用基本名称并按照您的意愿运行。这里有一些关于如何编写 custom upload handler.
的信息
然后您需要在 FILE_UPLOAD_HANDLERS
设置中定义您的处理程序。
编辑
Custom Upload Handlers 使用 Django 3.1
扩展上一个答案,从目录上传中获取完整路径的一种方法是将文件路径(已清除)中的斜杠(\
和 /
)替换为连字符:
class CustomMemoryFileUploadHandler(MemoryFileUploadHandler):
def new_file(self, *args, **kwargs):
args = (args[0], args[1].replace('/', '-').replace('\', '-')) + args[2:]
super(CustomMemoryFileUploadHandler, self).new_file(*args, **kwargs)
class CustomTemporaryFileUploadHandler(TemporaryFileUploadHandler):
def new_file(self, *args, **kwargs):
args = (args[0], args[1].replace('/', '-').replace('\', '-')) + args[2:]
super(CustomTemporaryFileUploadHandler, self).new_file(*args, **kwargs)
@csrf_exempt
def my_view(request):
# replace upload handlers. This depends on FILE_UPLOAD_HANDLERS setting. Below code handles the default in Django 1.10
request.upload_handlers = [CustomMemoryFileUploadHandler(request), CustomTemporaryFileUploadHandler(request)]
return _my_view(request)
@csrf_protect
def _my_view(request):
# if the path of the uploaded file was "test/abc.jpg", here it will be "test-abc.jpg"
blah = request.FILES[0].name
除了前面的答案之外,还有另一种可能对某人有用的方法。
如果请求中只有 一个文件,您可以获得 multipart/form-data
负载中的原始文件名,而无需覆盖处理程序。
MemoryFileUploadHandler
和 TemporaryFileUploadHandler
(默认使用,请参阅 Django's docs: Built-in upload handlers) are inherited from the FileUploadHandler
class. Such objects have the file_name
variable (see Django's code)。来自请求的文件之一的全名存储在这里(任何一个文件,我们不能提前说)。但是,如果您在请求中始终只有一个文件 - 就是这种方式。
因此视图将如下所示:
def your_view(request):
file = request.FILES.get('file_field')
full_file_name = request.upload_handlers[0].file_name # e.g. 'MainDir/SubDir1/1.jpg'
对于多文件上传,我们可以覆盖处理程序:
class NamedMemoryFileUploadHandler(MemoryFileUploadHandler):
def file_complete(self, file_size):
in_memory_file = super().file_complete(file_size)
if in_memory_file is None:
return
return in_memory_file, self.file_name
class NamedTemporaryFileUploadHandler(TemporaryFileUploadHandler):
def file_complete(self, file_size):
temporary_file = super().file_complete(file_size)
if temporary_file is None:
return
return temporary_file, self.file_name
@csrf_exempt
def upload_files(request):
request.upload_handlers = [
NamedMemoryFileUploadHandler(request),
NamedTemporaryFileUploadHandler(request),
]
return _upload_files(request)
@csrf_protect
def _upload_files(request):
files = request.FILES.getlist("file") # list of tuples [(<file1>, "'MainDir/SubDir1/1.jpg'"), (<file2>, "'MainDir/SubDir2/2.jpg'")]
for tmp_file, full_path in files:
...
我正在编写一个 django 应用程序来上传带有表单的文件目录。
这是我使用的允许上传目录的表格:
class FileFieldForm(forms.Form):
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs=
{'multiple': True, 'webkitdirectory': True, 'directory': True}))
这是原始的 post 负载:
------WebKitFormBoundaryPbO3HkrKGbBwgD3sd1
Content-Disposition: form-data; name="csrfmiddlewaretoken"
F575Bgl4U9dzgwePPeSW2ISZKk5c3CnRoqFasdasD0Hep6nD0LnAAObXbF92SUa96NbO2
------WebKitFormBoundaryPbO3HkrKGbBwgDsd31
Content-Disposition: form-data; name="file_field";
filename="MainDir/SubDir1/1.jpg"
Content-Type: image/jpeg
------WebKitFormBoundaryPbOasd3HkrKGbBwgD31
Content-Disposition: form-data; name="file_field";
filename="MainDir/SubDir2/2.jpg"
Content-Type: image/jpeg
这是处理表单的视图:
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'upload.html'
success_url = 'upload'
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
pprint("Name of file is " + f._get_name() + ' ' + f.field_name, sys.stderr)
new_file = FileModel(file=f)
new_file.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
问题是 django 中的文件对象名称没有子目录名称。我假设中间件处理请求之一是从文件名中解析和删除子目录名称。有什么方法可以获取包含目录和子目录名称的原始文件名?
我相信这就是 Django 的实现方式。请参考Django's Upload Handler doc.
它有默认的上传处理程序 MemoryFileUploadHandler
和 TemporaryFileUploadHandler
。它们都使用 UploadedFile
来处理文件,并且它有一个函数 _set_name
,它采用文件的基本名称。
甚至有评论说为什么它采用基本名称:
def _set_name(self, name):
# Sanitize the file name so that it can't be dangerous.
if name is not None:
# Just use the basename of the file -- anything else is dangerous.
name = os.path.basename(name)
# File names longer than 255 characters can cause problems on older OSes.
if len(name) > 255:
name, ext = os.path.splitext(name)
ext = ext[:255]
name = name[:255 - len(ext)] + ext
self._name = name
但我认为您可以编写自己的上传处理程序,它不采用基本名称并按照您的意愿运行。这里有一些关于如何编写 custom upload handler.
的信息然后您需要在 FILE_UPLOAD_HANDLERS
设置中定义您的处理程序。
编辑 Custom Upload Handlers 使用 Django 3.1
扩展上一个答案,从目录上传中获取完整路径的一种方法是将文件路径(已清除)中的斜杠(\
和 /
)替换为连字符:
class CustomMemoryFileUploadHandler(MemoryFileUploadHandler):
def new_file(self, *args, **kwargs):
args = (args[0], args[1].replace('/', '-').replace('\', '-')) + args[2:]
super(CustomMemoryFileUploadHandler, self).new_file(*args, **kwargs)
class CustomTemporaryFileUploadHandler(TemporaryFileUploadHandler):
def new_file(self, *args, **kwargs):
args = (args[0], args[1].replace('/', '-').replace('\', '-')) + args[2:]
super(CustomTemporaryFileUploadHandler, self).new_file(*args, **kwargs)
@csrf_exempt
def my_view(request):
# replace upload handlers. This depends on FILE_UPLOAD_HANDLERS setting. Below code handles the default in Django 1.10
request.upload_handlers = [CustomMemoryFileUploadHandler(request), CustomTemporaryFileUploadHandler(request)]
return _my_view(request)
@csrf_protect
def _my_view(request):
# if the path of the uploaded file was "test/abc.jpg", here it will be "test-abc.jpg"
blah = request.FILES[0].name
除了前面的答案之外,还有另一种可能对某人有用的方法。
如果请求中只有 一个文件,您可以获得 multipart/form-data
负载中的原始文件名,而无需覆盖处理程序。
MemoryFileUploadHandler
和 TemporaryFileUploadHandler
(默认使用,请参阅 Django's docs: Built-in upload handlers) are inherited from the FileUploadHandler
class. Such objects have the file_name
variable (see Django's code)。来自请求的文件之一的全名存储在这里(任何一个文件,我们不能提前说)。但是,如果您在请求中始终只有一个文件 - 就是这种方式。
因此视图将如下所示:
def your_view(request):
file = request.FILES.get('file_field')
full_file_name = request.upload_handlers[0].file_name # e.g. 'MainDir/SubDir1/1.jpg'
对于多文件上传,我们可以覆盖处理程序:
class NamedMemoryFileUploadHandler(MemoryFileUploadHandler):
def file_complete(self, file_size):
in_memory_file = super().file_complete(file_size)
if in_memory_file is None:
return
return in_memory_file, self.file_name
class NamedTemporaryFileUploadHandler(TemporaryFileUploadHandler):
def file_complete(self, file_size):
temporary_file = super().file_complete(file_size)
if temporary_file is None:
return
return temporary_file, self.file_name
@csrf_exempt
def upload_files(request):
request.upload_handlers = [
NamedMemoryFileUploadHandler(request),
NamedTemporaryFileUploadHandler(request),
]
return _upload_files(request)
@csrf_protect
def _upload_files(request):
files = request.FILES.getlist("file") # list of tuples [(<file1>, "'MainDir/SubDir1/1.jpg'"), (<file2>, "'MainDir/SubDir2/2.jpg'")]
for tmp_file, full_path in files:
...