Django目录上传获取子目录名称

时间:2016-11-17 12:18:59

标签: python django file-upload django-forms directory-upload

我正在编写一个django应用程序来上传带有表单的文件目录。

这是我正在使用的表单,允许上传目录:

class FileFieldForm(forms.Form):
    file_field = forms.FileField(widget=forms.ClearableFileInput(attrs=
        {'multiple': True, 'webkitdirectory': True, 'directory': True}))

这是原始的有效负载:

------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中文件对象的名称没有子目录名。我假设其中一个中间件处理请求正在解析并从filename中删除子目录名称。有没有办法我可以获得具有目录和子目录名称的原始文件名?

3 个答案:

答案 0 :(得分:3)

我相信这就是Django的实现方式。请参阅Django's Upload Handler doc

它有默认的上传处理程序MemoryFileUploadHandlerTemporaryFileUploadHandler。他们都使用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设置中定义处理程序。

答案 1 :(得分:0)

扩展上一个答案,从目录上传获取完整路径的一种方法是使用连字符替换文件路径中的斜杠(/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 )(使用清理消除):

y <- t(sapply(x, unlist))

答案 2 :(得分:0)

除了前面的答案外,还有另一种可能对某人有用的方法。 如果请求中只有一个文件,则可以获取multipart/form-data有效负载中的原始文件名,而不会覆盖处理程序。

MemoryFileUploadHandlerTemporaryFileUploadHandler(默认使用,请参见Django's docs: Built-in upload handlers)是从FileUploadHandler类继承的。此类对象具有file_name变量(请参见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:
        ...