clean() 方法导致文件使用 POST 形式丢失数据

clean() method causes files to lose data using POST form

我已经设置了一个表单和视图,可以一次将多个 *.gpx 文件上传到我的网站。这些文件使用表单上的 clean() 方法进行验证,然后在验证后传递给函数进行处理。

当我上传一些无效文件时,clean() 方法会捕获它们并按预期通知用户。

当我上传一些有效文件时,处理函数崩溃并显示文件为空的错误。

如果我注释掉 clean() 方法,则可以正常上传有效文件。

在 clean() 方法期间,除了意味着文件被清空之外,表单会发生什么情况?

这是我的表格:

class UploadGpxForm(forms.Form):

    gpx_file = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))

这是我的观点:

class UploadGpxView(FormView):
    form_class = UploadGpxForm
    template_name = 'dashboard/upload.html' # Replace with your template.
    success_url = reverse_lazy('dashboard:index')  # Replace with your URL or reverse().

    def post(self, request, *args, **kwargs):
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        files = request.FILES.getlist('gpx_file')
        if form.is_valid():
            for f in files:
                SaveGPXtoPostGIS(f)
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

这是我对 UploadGpxForm 的清理方法:

    def clean(self):

        file_errors=[]

        files = list(self.files.getlist('gpx_file'))

        for f in list(files):
            #check file has only one full stop in it.
            if len(f.name.split('.')) != 2:
                file_errors.append(ValidationError(
                    _('%(file_name)s has not been uploaded:'\
                    'File type is not supported')
                    , params = { 'file_name': f.name }
                    , code = 'file_type')
                    )

            #check file doesn't breach the file size listed in settings
            if f.content_type in settings.DASHBOARD_UPLOAD_FILE_TYPES:
                if f._size > settings.DASHBOARD_UPLOAD_FILE_MAX_SIZE:
                    file_errors.append(ValidationError(
                        _('%(file_name)s has not been uploaded: File too big.'\
                        'Please keep filesize under %(setting_size)s.'\
                        'Current filesize %(file_size)s') ,
                        params = {
                            'file_name': f.name,
                            'setting_size': filesizeformat(
                                settings.DASHBOARD_UPLOAD_FILE_MAX_SIZE),
                            'file_size': filesizeformat(f._size)
                            },
                        code = 'file_size'
                            )
                            )
            #check it is one of our allowed file types
            else:
                file_errors.append(ValidationError(
                    _('%(file_name)s has not been uploaded:'\
                    'File type is not supported')
                     , params = { 'file_name' : f.name }
                     , code = 'file_type'
                     )
                     )
            #next check the file hasn't been uploaded before
            #generate MD5
            md5hash = md5()
            for chunk in f.chunks():
                md5hash.update(chunk)
            file_hash = md5hash.hexdigest()

            if gpxTrack.objects.filter(file_hash=file_hash).exists():
                file_errors.append(ValidationError(
                    _('%(file_name)s has not been uploaded as a identical file'\
                    'has already been uploaded previously'),
                    params = { 'file_name' : f.name },
                    code = 'file_hash'))

        #finally raise errors if there are any
        if len(file_errors) > 0:
            raise ValidationError(file_errors)
        else:
            return files

当您读取文件内容(用于计算 md5 哈希)时,您需要使用 file.seek:

将文件对象的位置移动到开头(第 0 个字节)
md5hash = md5()
for chunk in f.chunks():
    md5hash.update(chunk)
file_hash = md5hash.hexdigest()
f.seek(0)  #<-- add this line