如何通过Axios向Django上传文件?切换工具后request.FILES为空求解
用Axios向Django上传文件的正确姿势
你之前遇到的问题核心在于:你传的是Blob URL字符串,而不是实际的文件对象,所以Django没法把它识别成上传文件,自然request.FILES是空的,而字符串会被放到request.POST里。下面我给你一步步讲怎么用react-dropzone + Axios正确上传文件到Django:
一、前端:直接用react-dropzone返回的File对象,构建FormData
react-dropzone的acceptedFiles返回的就是标准的File对象数组,不用转成Blob URL,直接用它来构建FormData就行:
import { useDropzone } from 'react-dropzone'; import axios from 'axios'; function FileUploadComponent() { const { getRootProps, getInputProps, acceptedFiles } = useDropzone(); const handleUpload = async () => { if (!acceptedFiles.length) return; // 1. 构建FormData,把文件加进去 const formData = new FormData(); // 这里的键名`uploaded_file`要和后端接收的字段名一致 formData.append('uploaded_file', acceptedFiles[0]); // 如果需要传其他参数,也可以一起加进去 // formData.append('description', '这是一张测试图片'); // 2. 发送Axios请求 try { const res = await axios.post('/api/upload/', formData, { headers: { // 重点:不要手动设置Content-Type!Axios会自动处理成multipart/form-data并带上正确的boundary // 手动设置反而会导致Django解析失败 'X-CSRFToken': getCsrfToken(), // 带上Django需要的CSRF令牌 }, }); console.log('上传成功!', res.data); } catch (err) { console.error('上传失败', err); } }; // 辅助函数:从cookie里取Django的CSRF令牌 const getCsrfToken = () => { return document.cookie.split('; ').find(row => row.startsWith('csrftoken='))?.split('=')[1]; }; return ( <div {...getRootProps()} style={{ border: '2px dashed #ccc', padding: '20px', cursor: 'pointer' }}> <input {...getInputProps()} /> <p>拖拽文件到这里,或点击选择文件</p> {acceptedFiles.length > 0 && ( <button onClick={handleUpload} style={{ marginTop: '10px' }}>开始上传</button> )} </div> ); } export default FileUploadComponent;
二、后端:Django接收文件的正确写法
确保你的视图能正确解析multipart/form-data请求,从request.FILES里取文件:
from django.http import JsonResponse from django.views.decorators.http import require_POST from django.core.files.storage import default_storage # 生产环境请确保CSRF保护生效,这里如果前端已经传了CSRF令牌,可以去掉csrf_exempt from django.views.decorators.csrf import csrf_exempt @csrf_exempt @require_POST def upload_file_view(request): # 从request.FILES里取文件,键名要和前端FormData的键一致 uploaded_file = request.FILES.get('uploaded_file') if not uploaded_file: return JsonResponse({'status': 'error', 'msg': '未上传文件'}, status=400) # 保存文件到Django的媒体目录(需要先在settings里配置MEDIA_ROOT和MEDIA_URL) file_path = default_storage.save(uploaded_file.name, uploaded_file) return JsonResponse({ 'status': 'success', 'file_name': uploaded_file.name, 'file_url': f'/media/{file_path}' })
三、关键注意事项
- 不要转Blob URL:直接用react-dropzone返回的
File对象,Blob URL是给前端预览用的,不是用来传后端的。 - 不要手动设置Content-Type:Axios在发送FormData时会自动生成正确的
multipart/form-data请求头,包含必要的boundary参数,手动设置会丢失这个参数,导致Django无法解析文件。 - CSRF令牌要带上:Django默认开启CSRF保护,前端必须在请求头里带上
X-CSRFToken,值从cookie里的csrftoken字段取。 - 配置Django媒体存储:在
settings.py里要设置:
还要在项目的urls.py里添加媒体文件的路由(仅开发环境用,生产环境用Nginx等托管):MEDIA_ROOT = BASE_DIR / 'media' MEDIA_URL = '/media/'from django.conf import settings from django.conf.urls.static import static urlpatterns = [ # 你的其他路由 ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
这样调整后,你就能在Django的request.FILES里拿到上传的文件了,不会再是空的MultiValueDict啦~
内容的提问来源于stack exchange,提问作者Horai Nuri
相关产品推荐
相关产品推荐

