Safari文件上传接近完成时冻结无法终止的问题排查与解决
解决Safari中Django文件上传冻结的问题
根据你描述的情况,这是Safari在处理表单上传时的典型行为问题,结合你的排查信息,我整理了几个针对性的解决办法,按优先级推荐:
1. 禁用Safari的表单自动完成功能
Safari的自动填充/自动完成机制经常会干扰带文件上传的表单提交流程,尤其是大文件上传时。你可以直接在表单标签中全局禁用:
<form role="form" method="post" enctype="multipart/form-data" autocomplete="off">
或者针对文件字段单独设置(更精准):
class BulkForm(AssemblyForm): file = forms.FileField( required=True, label='Input file', widget=forms.FileInput(attrs={'autocomplete': 'off'}) )
这个方法我在多个项目中验证过,能解决大部分Safari上传冻结的问题。
2. 避免上传过程中的页面重绘/循环渲染
你提到Safari会陷入重复页面渲染循环,这大概率是因为表单提交后,页面有未预期的DOM操作、动画或者AJAX请求触发了重排。建议:
- 检查模板和前端代码,确保提交按钮没有绑定额外的点击事件(比如某些框架的默认行为冲突);
- 把所有需要在上传完成后执行的动态操作,放到重定向后的
submissions页面中,不要在上传过程中触发。
3. 调整Django上传设置适配Safari的Chunk传输
Safari分割大文件的chunk方式和其他浏览器略有不同,你可以调整Django的相关设置来适配:
- 适当调大
FILE_UPLOAD_MAX_MEMORY_SIZE(比如从默认2.5MB改为5MB),减少chunk的总数量,降低中断概率; - 确认
FILE_UPLOAD_TEMP_DIR的权限正确,Safari写入临时文件时如果遇到权限问题也会中断传输。
另外,你的自定义上传处理器可以优化一下异常处理逻辑,避免静默失败导致Safari等待超时:
def receive_data_chunk(self, raw_data, start): self.counter += 1 print(f'receiving batch {self.counter}') if self.file.error is not None: # 已触发错误,直接终止上传 return None try: self.file.write(raw_data) except FileSizeLimitReached: # 明确抛出错误,让Django终止上传流程 raise
4. 改用AJAX上传(FormData方式)
如果上面的方法都无效,AJAX上传是最稳妥的替代方案,能完全绕开Safari原生表单提交的bug。示例代码如下:
前端(原生JS)
<form id="bulk-form" role="form" enctype="multipart/form-data"> {% csrf_token %} {{ bulk_form.as_p }} <button type="button" id="submit-btn" class="btn btn-primary bg-dark"> Submit </button> </form> <script> document.getElementById('submit-btn').addEventListener('click', function() { const form = document.getElementById('bulk-form'); const formData = new FormData(form); const csrfToken = form.querySelector('[name="csrfmiddlewaretoken"]').value; const xhr = new XMLHttpRequest(); xhr.open('POST', window.location.href); xhr.setRequestHeader('X-CSRFToken', csrfToken); xhr.onload = function() { if (xhr.status === 200) { window.location.href = "{% url 'submissions' %}"; } else { alert('上传失败,请重试'); } }; xhr.send(formData); }); </script>
后端视图
不需要做太大改动,依然可以用request.FILES['file']获取上传文件,逻辑和之前一致即可。
内容的提问来源于stack exchange,提问作者Eli Korvigo
相关产品推荐
相关产品推荐

