【问题标题】:Django directory upload get sub-directory namesDjango目录上传获取子目录名称
【发布时间】:2017-03-31 23:36:38
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

    我相信这就是 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 设置中定义您的处理程序。

    编辑 Custom Upload Handlers 使用 Django 3.1

    【讨论】:

    • 我在文档中错过了这一点。我会试试这个,它应该可以工作。
    • 我不明白如何实现它你能用一个工作示例扩展你的答案吗?
    【解决方案2】:

    扩展上一个答案,从目录上传获取完整路径的一种方法是用连字符替换文件路径中的斜杠(\/):

    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
    

    【讨论】:

      【解决方案3】:

      除了前面的答案之外,还有另一种可能对某人有用的方法。 如果请求中只有一个文件,则无需覆盖处理程序即可获取 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:
              ...
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-10
      • 1970-01-01
      • 1970-01-01
      • 2011-12-08
      • 2011-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多