在Safari中,文件上载不会终止(在接近结束时冻结)

2024-06-06 12:46:55 发布

您现在位置:Python中文网/ 问答频道 /正文

我在Mac OS X上运行的测试服务器是最新的Django 2版本(Python3.6)。我的应用程序在Safari 11.0.2和Chrome(build 63)上都运行得很好,除了一个细节。我有这个文件上传表:

class BulkForm(AssemblyForm):  # AssemblyForm itself defines a Char field.
    file = forms.FileField(required=True, label='Input file')

这是由以下视图处理的(在本例中,我将其减少到最小值):

def formtest(request):
    if request.method == 'POST':
        form = BulkForm(request.POST, request.FILES)
        if form.is_valid():
            upload = request.FILES['file']
            return HttpResponseRedirect(reverse('submissions'))
    bulk = BulkForm()
    return render(request, 'upload.html', {'bulk_form': bulk})

以及模板:

...
                <div class="tab-pane fade" id="bulk">
                    <div class="container my-4">
                        <form role="form" method="post"
                              enctype="multipart/form-data">
                            {% csrf_token %}
                            {{ bulk_form.as_p }}
                            <button type="submit" name="bulk-form"
                                    class="btn btn-primary bg-dark">
                                Submit
                            </button>
                        </form>
                    </div>
...

在Safari中,即使我尝试相同的文件,上传也会冻结:有时上传成功,有时冻结。文件大小与其卡住的可能性之间存在相关性。为了调查这个问题,我实现了自己的上载处理程序:

class SizeLimitedUploadedFile(uploadedfile.UploadedFile):
    def __init__(self, name, content_type, size, charset, content_type_extra=None):
        file = tempfile.NamedTemporaryFile(
            buffering=settings.FILE_UPLOAD_MAX_MEMORY_SIZE, delete=False,
            suffix=f'.upload.{name.split(".", 1)[1]}',
            dir=settings.FILE_UPLOAD_TEMP_DIR
        )
        super().__init__(file, file.name, content_type, 0, charset, content_type_extra)
        self.error = None

    def write(self, chunk: bytes) -> int:
        """
        :param chunk: bytes to write
        :return: the number of bytes written
        :raises FileSizeLimitReached: if file size reached
        """
        newsize = self.size + len(chunk)
        if newsize <= settings.FILE_UPLOAD_MAX_MEMORY_SIZE:
            self.file.write(chunk)
            self.size = newsize
            return len(chunk)
        self.error = FileSizeLimitReached
        raise FileSizeLimitReached

    def writelines(self, chunks: Iterable[bytes]):
        """
        :param chunks: bytes to write
        :raises FileSizeLimitReached: if file size reached
        """
        for chunk in chunks:
            self.write(chunk)


class TestFileUploadHandler(uploadhandler.FileUploadHandler):
    """
    File upload handler to stream uploads into memory (used for small files).
    """

    def new_file(self, *args, **kwargs):
        """
        Create the file object to append to as data are coming in.
        """
        print('creating new')
        super().new_file(*args, **kwargs)
        self.file = SizeLimitedUploadedFile(
            self.file_name, self.content_type, 0,
            self.charset, self.content_type_extra
        )
        self.counter = 0

    def receive_data_chunk(self, raw_data, start):
        self.counter += 1
        print(f'receiving batch {self.counter}')
        if self.file.error is None:
            with suppress(FileSizeLimitReached):
                self.file.write(raw_data)

    def file_complete(self, file_size):
        print('complete')
        self.file.seek(0)
        return self.file

然后,我替换了settings.py中的默认上载处理程序,并多次尝试上载同一个文件。当上传按预期终止时,我总是看到1250个对TestFileUploadHandler.receive_data_chunk的调用。当它冻结时,柜台停留在1239-1245个电话左右。我只在野生动物园见过这种行为。Chrome与内置处理程序以及我的自定义处理程序都工作得很好—它从不冻结。我试着在网上搜索这个,到目前为止,最热门的是2009年的一个thread关于一个旧的WebKit bug。我一直跟着线索走,但毫无结果。我怎样才能解决这个问题?你知道吗

更新

我已经在最新版本的Firefox和Opera上运行了测试。两者都完美无瑕。似乎这个问题与Safari无关。你知道吗

更新2

我在上传文件的过程中分析了Safari的活动。不管出于什么原因,它有时会进入一个无休止的重复页面呈现循环。你知道吗


Tags: selfformdatasizereturnifrequestdef