我在Mac OS X上运行最新的Django 2版本(Python3.6)下的测试服务器。我的应用程序似乎在Safari 11.0.2和Chrome(版本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
中替换了默认的上传处理程序,并尝试多次上传同一个文件。当上传按预期终止时,我总是会看到对TestFileUploadHandler.receive_data_chunk
的1250次通话。当它冻结时,计数器会保持1239-1245左右的呼叫。我只在Safari中看到过这种行为。 Chrome已经与内置处理程序以及我的自定义处理程序一起运行良好 - 它永远不会冻结。我已经尝试在网上搜索这个,到目前为止,最接近的打击是从2009年开始thread关于一个旧的WebKit错误。我跟着线程无济于事。我怎样才能解决这个问题?
更新
我已经对最新版本的Firefox和Opera进行了测试。两者都完美无缺。似乎这个问题与Safari隔离。
更新2
我在文件上传期间描述了Safari活动。无论出于何种原因,它有时会进入无限循环的重复页面渲染。