Django如何基于Google Drive API实现Google Drive文件上传下载
Django集成Google Drive API实现凭证管理、文件上传下载全流程
以下是可直接落地的代码实现,适配Django多用户Web场景,替换了官方快速入门里仅适合单用户本地脚本的文件存储token逻辑。
前置依赖安装
先安装Google API相关依赖包:
pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib
一、Model层实现
Web场景下不能用官方示例里的本地token.json存凭证,必须绑定Django用户存入数据库,避免多用户凭证串号。在你的app下的models.py添加以下代码:
from django.db import models from django.contrib.auth.models import User from google.oauth2.credentials import Credentials class GoogleDriveToken(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='drive_token') credentials = models.JSONField() # 存储OAuth2凭证全量字段 created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def get_credentials(self): """从数据库存储的JSON数据还原Google Credentials对象""" return Credentials(**self.credentials) def save_credentials(self, creds): """将Credentials对象序列化为JSON存入数据库""" self.credentials = { 'token': creds.token, 'refresh_token': creds.refresh_token, 'token_uri': creds.token_uri, 'client_id': creds.client_id, 'client_secret': creds.client_secret, 'scopes': creds.scopes } self.save()
写完记得执行python manage.py makemigrations和python manage.py migrate生成数据库表。
二、基础配置
在项目settings.py中添加Google API相关配置:
# Google Drive API 配置 GOOGLE_CLIENT_SECRETS_FILE = "path/to/your/client_secret.json" # 替换为你从GCP控制台下载的OAuth客户端密钥文件路径 GOOGLE_DRIVE_SCOPES = ['https://www.googleapis.com/auth/drive.file'] # 最小权限:仅能访问本应用上传的文件 GOOGLE_REDIRECT_URI = "http://localhost:8000/drive/oauth2callback" # 授权回调地址,需和GCP控制台配置的一致 # 本地调试时加这行,允许HTTP请求走OAuth流程,生产环境必须删除,强制用HTTPS os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
三、View层业务逻辑
所有视图加登录校验,确保凭证和用户绑定。在app的views.py添加以下代码:
公共工具方法
先写复用的服务实例构建方法,自动处理凭证过期刷新:
from django.shortcuts import redirect, render from django.contrib.auth.decorators import login_required from django.http import FileResponse, Http404 from google_auth_oauthlib.flow import Flow from googleapiclient.discovery import build from googleapiclient.http import MediaFileUpload, MediaIoBaseDownload from google.auth.transport.requests import Request from .models import GoogleDriveToken from django.conf import settings import io import os def _get_drive_service(user): """获取当前用户授权的Drive服务实例,自动刷新过期凭证""" try: token_obj = GoogleDriveToken.objects.get(user=user) creds = token_obj.get_credentials() if creds.expired and creds.refresh_token: creds.refresh(Request()) token_obj.save_credentials(creds) return build('drive', 'v3', credentials=creds) except GoogleDriveToken.DoesNotExist: return None
1. 授权跳转视图
@login_required def drive_auth(request): """跳转至Google授权页面""" flow = Flow.from_client_secrets_file( settings.GOOGLE_CLIENT_SECRETS_FILE, scopes=settings.GOOGLE_DRIVE_SCOPES, redirect_uri=settings.GOOGLE_REDIRECT_URI ) authorization_url, state = flow.authorization_url( access_type='offline', # 必须配置,才能拿到refresh_token,后续无需用户重复授权 include_granted_scopes='true', prompt='consent' # 首次授权强制弹出确认页,确保拿到refresh_token ) request.session['google_auth_state'] = state # 存state到session,回调时做CSRF校验 return redirect(authorization_url)
2. 授权回调视图
@login_required def oauth2callback(request): """处理Google授权回调,存储用户凭证""" state = request.session.pop('google_auth_state', None) flow = Flow.from_client_secrets_file( settings.GOOGLE_CLIENT_SECRETS_FILE, scopes=settings.GOOGLE_DRIVE_SCOPES, state=state, redirect_uri=settings.GOOGLE_REDIRECT_URI ) flow.fetch_token(authorization_response=request.build_absolute_uri()) creds = flow.credentials # 更新或创建当前用户的凭证记录 token_obj, _ = GoogleDriveToken.objects.get_or_create(user=request.user) token_obj.save_credentials(creds) return redirect('drive_upload')
3. 文件上传视图
@login_required def upload_to_drive(request): service = _get_drive_service(request.user) if not service: return redirect('drive_auth') # 未授权先跳转授权流程 if request.method == 'POST': uploaded_file = request.FILES.get('file') if not uploaded_file: return render(request, 'drive/upload.html', {'error': '请选择要上传的文件'}) # 临时存储上传文件,上传完成后删除 temp_path = f"/tmp/{uploaded_file.name}" with open(temp_path, 'wb+') as f: for chunk in uploaded_file.chunks(): f.write(chunk) try: file_metadata = {'name': uploaded_file.name} media = MediaFileUpload(temp_path, resumable=True) # 调用Drive API上传文件 uploaded_res = service.files().create( body=file_metadata, media_body=media, fields='id, name, webViewLink' ).execute() return render(request, 'drive/upload.html', { 'success': f"文件上传成功,文件ID:{uploaded_res.get('id')},访问链接:{uploaded_res.get('webViewLink')}" }) finally: if os.path.exists(temp_path): os.remove(temp_path) return render(request, 'drive/upload.html')
4. 文件下载视图
@login_required def download_from_drive(request, file_id): service = _get_drive_service(request.user) if not service: return redirect('drive_auth') try: # 先获取文件元信息取原始文件名 file_meta = service.files().get(fileId=file_id, fields='name').execute() file_name = file_meta.get('name') # 流式下载文件到内存 drive_request = service.files().get_media(fileId=file_id) fh = io.BytesIO() downloader = MediaIoBaseDownload(fh, drive_request) done = False while not done: _, done = downloader.next_chunk() fh.seek(0) return FileResponse(fh, as_attachment=True, filename=file_name) except Exception: raise Http404("文件不存在或无访问权限")
四、路由配置
在app的urls.py中添加路由:
from django.urls import path from . import views urlpatterns = [ path('drive/auth/', views.drive_auth, name='drive_auth'), path('drive/oauth2callback/', views.oauth2callback, name='oauth2callback'), path('drive/upload/', views.upload_to_drive, name='drive_upload'), path('drive/download/<str:file_id>/', views.download_from_drive, name='drive_download'), ]
配套上传模板
在templates/drive/路径下新建upload.html,写一个简单的上传表单即可:
<!DOCTYPE html> <html> <body> <h2>上传文件到Google Drive</h2> <form method="post" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="file" required> <button type="submit">提交上传</button> {% if error %}<p style="color: red;">{{ error }}</p>{% endif %} {% if success %}<p style="color: green;">{{ success|safe }}</p>{% endif %} </form> </body> </html>
注意事项
- 生产环境必须删除
OAUTHLIB_INSECURE_TRANSPORT配置,强制使用HTTPS,否则会有安全风险 - 不要将
client_secret.json提交到公开代码仓库,生产环境建议将凭证信息存在环境变量中,避免硬编码 - 若需要上传大文件,可调整Django的
FILE_UPLOAD_MAX_MEMORY_SIZE配置,避免上传被框架拦截 - 若需要访问用户Drive下的所有文件,可将scope修改为
https://www.googleapis.com/auth/drive,但建议遵循最小权限原则使用默认的drive.file权限
内容的提问来源于stack exchange,提问作者NEEHARIKA RAMANUJULA
相关产品推荐
相关产品推荐

