You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flask Web应用流式生成ZIP文件时浏览器下载进度条的Content-Length不匹配问题

Flask Web应用流式生成ZIP文件时浏览器下载进度条的Content-Length不匹配问题

我正在开发一个Flask Python Web应用,核心功能是根据用户设置的筛选条件,把一批文件打包成ZIP后提供下载。但遇到了一个头疼的问题:用户点击下载按钮后,后端需要拉取所有目标文件并开始创建ZIP,这个过程可能要耗时好几分钟,用户完全没法判断应用是在正常处理还是已经挂掉了。

我想到的解决方案是流式传输生成中的ZIP文件——这样既能更快地把文件内容发送给用户,也能让用户感知到应用正在工作。但这里有个关键问题:要让浏览器自带的下载栏(就是那个弹出的小窗口或者带进度条的下载页面)正常显示进度,必须在响应头里提供Content-Length,可ZIP文件还没生成完成,我根本没法知道最终的文件大小。

我本来以为估算大小会很简单,因为我用的是ZIP_STORED(无压缩)模式,只需要把所有文件的大小加起来,再加上ZIP的结构开销就行。于是我写了个计算总大小的函数,给每个文件加了22字节的开销,再给中央目录加了22字节,但实际测试下来,浏览器还是会返回ERR_CONTENT_LENGTH_MISMATCH错误,直接拒绝下载。看来ZIP的内部结构开销比我预估的要复杂。

目前我有几个备选方案:

  • 做一个基于Server Sent Event(SSE)的自定义进度条:开一个单独的/progress路由,前端通过轮询这个接口获取已发送的字节数,从而更新进度。但我真的特别想用浏览器自带的下载栏,这算是我的一个小执念了。
  • 放弃流式传输:先在后台完整生成ZIP文件,用SSE给用户实时更新生成进度,等文件完全生成后,再带着准确的Content-Length头把文件发送给用户。但这个方案的体验不如流式传输那么流畅。

附上我用来估算大小和生成ZIP条目的代码:

import os
from datetime import datetime
from zipfile import ZIP_64, ZIP_STORED

def calculate_total_size(filenames):
    total_size = 0
    for file in filenames:
        matching_filepath, _ = get_file_info(file)
        if matching_filepath:
            total_size += os.path.getsize(matching_filepath)

    # Add overhead for ZIP file structure (22 bytes per file + 22 bytes for the central directory)
    total_size += 22 * (len(filenames) + 1)
    return total_size

def generate_file_entries(filenames):
    for file in filenames:
        matching_filepath, filename = get_file_info(file)
        if matching_filepath:
            file_stat = os.stat(matching_filepath)
            modified_at = datetime.utcfromtimestamp(file_stat.st_mtime)
            with open(matching_filepath, 'rb') as f:
                chunk = f.read()
                if isinstance(chunk, bytes):  # Ensure only bytes are yielded
                    yield filename, modified_at, 0o600, ZIP_64, [chunk]
                else:
                    print(f"Unexpected data type for file contents: {type(chunk)}")

备注:内容来源于stack exchange,提问作者john stamos

火山引擎 最新活动