解决 Fetch API 下载视频文件大小为 0 字节的问题

解决 Fetch API 下载视频文件大小为 0 字节的问题
最新回答
喵呜狸

2023-06-11 14:57:47

使用 Fetch API 下载视频文件大小为 0 字节的问题通常由 mode: 'no-cors' 配置或不必要的 Content-Type 请求头引起,可通过移除这些配置并优化请求逻辑解决。

问题根源分析
  1. mode: 'no-cors' 的副作用

    该模式会使响应变为 opaque(不透明),导致无法通过 response.blob() 或 response.json() 读取内容,最终生成空文件。

    浏览器直接访问 URL 时不受此限制,但 Fetch API 的 no-cors 模式会主动屏蔽响应数据。

  2. 不必要的 Content-Type 请求头

    Content-Type 用于标识 请求体 的数据类型(如 POST 上传文件时),而下载文件时服务器会通过响应头返回正确的类型。

    手动设置 Content-Type: 'video/mp4' 可能干扰服务器处理,导致响应异常。

正确实现步骤
  1. 移除 mode: 'no-cors'

    使用默认的 cors 模式,确保服务器配置了正确的 CORS 策略(如 Access-Control-Allow-Origin: *)。

    若服务器未配置 CORS,请求会直接失败(而非生成空文件),便于调试。

  2. 删除不必要的请求头

    仅在需要时(如身份验证)添加请求头,避免手动设置 Content-Type。

  3. 完整代码示例

async function downloadVideo(link) { try { // 1. 发起默认 cors 模式的请求,无额外请求头 const response = await fetch(link); // 2. 检查响应状态码 if (!response.ok) { throw new Error(`HTTP 错误! 状态码: ${response.status}`); } // 3. 读取响应为 Blob 对象 const blob = await response.blob(); // 4. 创建 Blob 的临时 URL const url = URL.createObjectURL(blob); // 5. 创建 <a> 元素触发下载 const a = document.createElement('a'); a.href = url; a.download = 'video.mp4'; // 设置默认文件名 document.body.appendChild(a); a.click(); // 6. 清理资源 document.body.removeChild(a); URL.revokeObjectURL(url); console.log('视频下载成功!'); } catch (error) { console.error('下载失败:', error); // 可添加用户提示逻辑 }}// 示例调用(替换为实际视频链接)// downloadVideo('
https://example.com/video.mp4'
);关键点解析
  • response.ok 检查确保仅处理 HTTP 状态码为 200-299 的响应,避免因 404 或 500 错误导致后续操作失败。

  • response.blob() 的作用将二进制响应体转换为 Blob 对象,适用于视频、图片等文件的处理。

  • URL.createObjectURL() 与 revokeObjectURL()

    创建的 URL 仅在当前文档有效,需在下载完成后释放内存。

    忽略释放可能导致内存泄漏,尤其在频繁下载时。

  • CORS 策略要求若视频托管在跨域服务器上,需确保服务器返回以下响应头:

    Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET
常见问题扩展
  1. 如何处理大文件下载?对于超大文件,可使用 ReadableStream 分块读取并写入本地文件(需浏览器支持 Streams API),避免内存溢出。

  2. 如何显示下载进度?通过 response.body.getReader() 读取流数据,结合进度条组件更新进度:

    const reader = response.body.getReader();let receivedLength = 0;const totalLength = +response.headers.get('Content-Length');while (true) { const { done, value } = await reader.read(); if (done) break; receivedLength += value.length; console.log(`下载进度: ${Math.round((receivedLength / totalLength) * 100)}%`);}
  3. 为什么浏览器直接访问 URL 能下载,但 Fetch 失败?浏览器直接访问时可能绕过了 CORS 限制(如简单请求),而 Fetch API 默认遵循同源策略,需服务器明确允许跨域。

总结
  • 核心原则:避免 no-cors 模式和不必要的请求头,确保服务器 CORS 配置正确。
  • 优化方向:添加错误处理、资源清理和进度反馈,提升代码健壮性。
  • 扩展场景:大文件下载需结合流式处理,跨域问题需服务器端配合解决。