You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:51:26