贝利信息

如何使用 Range 请求高效下载指定字节数的远程资源

日期:2026-01-19 00:00 / 作者:心靈之曲

本文介绍如何通过 http `range` 请求头精准获取 url 资源的前 n 字节(如 1 mb),避免浏览器持续下载冗余数据,并解决重复调用时失效的问题。相比流式读取 + `reader.cancel()`,服务端支持的分块请求更可靠、可重入且无需手动缓冲拼接。

在前端开发中,若仅需下载远程文件的前一部分(例如校验签名、解析头部元信息或预览内容),盲目拉取完整资源不仅浪费带宽,还可能因 fetch 流未正确终止导致后续请求失败——正如原代码中反复调用 download() 后出现的异常:reader.cancel() 并不能真正中断底层网络连接,且已消耗的响应体可能影响缓存状态或服务端连接复用,造成后续请求挂起或返回不完整响应。

推荐方案:优先使用 HTTP R

ange 请求

现代 Web 服务器(如 Nginx、Apache、CDN 及多数静态文件托管服务)普遍支持 Range 请求头。通过声明 Range: bytes=0-N,我们可直接向服务端申明“只需第 0 到第 N 字节”,服务端将返回 206 Partial Content 响应,且只传输指定范围的数据:

async function downloadRange(url, maxBytes) {
  try {
    const response = await fetch(url, {
      headers: {
        Range: `bytes=0-${maxBytes - 1}`
      }
    });

    if (response.status === 206) {
      // 成功获取部分数据
      const arrayBuffer = await response.arrayBuffer();
      const result = new Uint8Array(arrayBuffer);
      const str = new TextDecoder().decode(result);
      console.log(`成功下载 ${result.length} 字节:`, str.substring(0, 100) + '...');
      return result;
    } else if (response.status === 200) {
      // 服务端不支持 Range,返回了完整响应(如小文件)
      console.warn('Server does not support Range; full response received.');
      const arrayBuffer = await response.arrayBuffer();
      return new Uint8Array(arrayBuffer).slice(0, maxBytes);
    } else {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`);
    }
  } catch (error) {
    console.error('Download failed:', error);
    throw error;
  }
}

优势显著

⚠️ 注意事项与兜底策略

综上,优先采用 Range 请求是解决“下载前 N 字节”问题的最佳实践。它简洁、标准、高效且健壮。仅当明确确认目标服务不支持分块时,再考虑基于 ReadableStream 的流控方案,并务必配合 AbortController 与超时机制增强鲁棒性。