DRF向客户端发送视频文件下载时客户端请求报错问题咨询
这种浏览器直接访问正常但客户端AJAX请求炸锅的情况我太熟了,大概率逃不过下面这几个坑,咱们挨个排查:
1. 先盯紧CORS跨域问题
浏览器直接输入URL属于简单请求,不会触发CORS预检,但客户端发起的XMLHttpRequest/fetch请求如果是跨域场景(比如前后端分离部署),就会触发浏览器的CORS校验。
你可以打开浏览器开发者工具的Network面板,查看客户端请求的响应头:
- 有没有
Access-Control-Allow-Origin头,值是否包含你的客户端域名? - 有没有
Access-Control-Allow-Methods,是否包含GET(毕竟下载是GET请求)?
如果没有这些头,服务器端必须补上。比如用Express的话,直接用cors()中间件;如果是Python Flask,就手动设置响应头:
@app.after_request def add_cors_headers(response): response.headers['Access-Control-Allow-Origin'] = 'http://your-client-domain.com' response.headers['Access-Control-Allow-Methods'] = 'GET' return response
2. 客户端请求的响应类型必须设对
下载视频是二进制数据,而很多HTTP请求库(比如Angular HttpClient、Axios)默认的响应类型是json,这会导致客户端试图把二进制文件当成JSON解析,直接报错0 Unknown Error。
Angular HttpClient示例:
this.http.get('http://localhost:8000/api/v1/store/download/?q=xxx', { responseType: 'blob' // 必须设为blob }).subscribe(blob => { // 生成下载链接 const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'target-video.mp4'; // 自定义文件名 a.click(); window.URL.revokeObjectURL(url); // 释放资源 });
Axios示例:
axios.get('http://localhost:8000/api/v1/store/download/?q=xxx', { responseType: 'blob' }).then(res => { const url = window.URL.createObjectURL(res.data); const a = document.createElement('a'); a.href = url; a.download = 'target-video.mp4'; a.click(); window.URL.revokeObjectURL(url); });
3. URL参数的特殊字符要编码
你的请求参数q里包含逗号(,)和斜杠(/)这些特殊字符,浏览器直接访问会自动编码,但客户端拼接URL时如果没手动编码,可能导致服务器解析参数出错。
一定要用encodeURIComponent()编码参数值:
const qParam = 'e8000074ab4d,eJwLyygpKbDS18/JT07MycgvLrGyMDAw0C8tyMlPTNFPy8xJ1U9M1MstMOEqMODSAwB4gQ8P'; const encodedQ = encodeURIComponent(qParam); const finalUrl = `http://localhost:8000/api/v1/store/download/?q=${encodedQ}`;
4. 服务器端的响应头要配置正确
服务器返回文件时,必须设置正确的Content-Disposition和Content-Type头,告诉浏览器这是需要下载的文件,而非直接播放的资源。
比如Flask的示例:
from flask import send_file, request @app.route('/api/v1/store/download/') def download_file(): q = request.args.get('q') # 根据q获取文件路径 file_path = get_file_path_by_hash(q) return send_file( file_path, mimetype='video/mp4', # 对应你的视频类型,比如video/avi、video/mov as_attachment=True, # 关键:触发下载而非播放 download_name='your-video.mp4' # 自定义下载文件名 )
5. 检查客户端的请求拦截器
如果你的项目用了请求拦截器(比如Angular的HttpInterceptor、Axios的interceptors),有可能拦截器修改了请求头或者错误地处理了响应。可以暂时禁用拦截器测试,如果能正常下载,那就是拦截器的逻辑有问题,比如添加了不必要的自定义头,或者在响应阶段提前抛出了错误。
先从CORS配置和responseType设置这两个最常见的坑开始排查,基本能解决大部分问题。
内容的提问来源于stack exchange,提问作者jicke

