You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用urllib2通过单次HTTP请求批量下载文件而非逐个请求?

如何使用urllib2通过单次HTTP请求批量下载文件而非逐个请求?

问题背景

我用Tornado搭建后端服务,客户端基于Jython 2.5.2(只能用urllib2)。之前实现了一个批量下载文本文件的功能:后端把所有文本文件的文件名和内容用分隔符拼接后一次性返回,客户端再拆分还原成文件,效果很好。但加入非文本文件后就失效了。后来改成每个文件单独发请求下载,虽然能工作但速度特别慢。想找一种既能批量传输又支持非文本文件的方法。

原后端代码(仅支持文本):

class DownloadHandler(web.RequestHandler):
    def get(self):
        files_as_text = ""
        for file in os.listdir("files"):
            files_as_text += file+"---title_split---"+open("files/"+file).read()+"---file_split---"
        self.write(files_as_text)

原客户端代码:

for uptodate_file in uptodate_files.split("---file_split---")[:-1]:
    uptodate_filename, uptodate_file_contents = uptodate_file.split("---title_split---")
    new_file = open(dir+uptodate_filename, 'wb')
    new_file.write(uptodate_file_contents).read())
    new_file.close()

改用单个请求下载的客户端代码(慢但有效):

urllib2.urlopen(url+uptodate_filename).read()

解决方案

针对你的需求,有两种实用方案,既保留批量传输的高效性,又能兼容所有文件类型:

方案1:Base64编码统一传输(简单易实现)

核心思路是把所有文件(文本+非文本)的内容转成Base64编码的字符串——Base64会把二进制数据转换成纯ASCII字符,这样就能安全地用分隔符拼接,不会破坏原始数据。

后端(Tornado)修改代码:
import base64
import os
from tornado import web

class DownloadHandler(web.RequestHandler):
    def get(self):
        files_data = []
        for file_name in os.listdir("files"):
            file_path = os.path.join("files", file_name)
            # 二进制读取文件并转Base64(转成字符串方便拼接)
            with open(file_path, "rb") as f:
                file_content = base64.b64encode(f.read()).decode("utf-8")
            # 拼接文件名和编码后的内容
            files_data.append(f"{file_name}---title_split---{file_content}")
        # 用分隔符连接所有文件数据
        self.write("---file_split---".join(files_data))
客户端(Jython)修改代码:
import urllib2
import base64
import os

target_dir = "./your_target_dir/"  # 替换成你的目标目录
response = urllib2.urlopen(your_download_url).read()

for file_block in response.split("---file_split---")[:-1]:
    filename, encoded_content = file_block.split("---title_split---")
    # 解码Base64并写入文件
    with open(os.path.join(target_dir, filename), "wb") as new_file:
        new_file.write(base64.b64decode(encoded_content))

方案2:自定义二进制包格式(传输效率更高)

如果文件体积大、数量多,Base64的33%体积开销可能会影响速度,这时可以用自定义二进制结构打包所有文件,直接传输二进制数据,没有额外体积损耗。

后端(Tornado)修改代码:
import os
import struct
from tornado import web

class DownloadHandler(web.RequestHandler):
    def get(self):
        # 初始化二进制数据缓冲区
        batch_data = b""
        for file_name in os.listdir("files"):
            file_path = os.path.join("files", file_name)
            with open(file_path, "rb") as f:
                file_content = f.read()
            
            # 按「文件名长度(4字节大端整数) + 文件名 + 文件内容长度(4字节大端整数) + 文件内容」的格式打包
            filename_bytes = file_name.encode("utf-8")
            batch_data += struct.pack(">I", len(filename_bytes))  # 大端字节序保证跨平台兼容
            batch_data += filename_bytes
            batch_data += struct.pack(">I", len(file_content))
            batch_data += file_content
        
        # 设置响应头为二进制流
        self.set_header("Content-Type", "application/octet-stream")
        self.write(batch_data)
客户端(Jython)修改代码:
import urllib2
import struct
import os

target_dir = "./your_target_dir/"
response_data = urllib2.urlopen(your_download_url).read()
offset = 0

while offset < len(response_data):
    # 读取文件名长度(4字节)
    filename_len = struct.unpack(">I", response_data[offset:offset+4])[0]
    offset += 4
    # 读取文件名
    filename = response_data[offset:offset+filename_len].decode("utf-8")
    offset += filename_len
    # 读取文件内容长度(4字节)
    content_len = struct.unpack(">I", response_data[offset:offset+4])[0]
    offset += 4
    # 读取文件内容
    file_content = response_data[offset:offset+content_len]
    offset += content_len
    
    # 写入本地文件
    with open(os.path.join(target_dir, filename), "wb") as new_file:
        new_file.write(file_content)

方案选择建议

  • 如果追求实现简单、代码改动小,选Base64方案,适合文件数量不多、体积不大的场景;
  • 如果追求极致传输效率,避免Base64的体积开销,选二进制打包方案,适合大文件或大量文件的场景。

备注:内容来源于stack exchange,提问作者Curtis C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 19:29:32