如何在Django项目中将图片存到Amazon S3、视频存到Vimeo
解决Django中视频上传至Vimeo而图片存S3的问题
嘿,我懂你遇到的困境——upload_to确实没法拿到文件的绝对路径,因为这个方法触发时文件还没完成持久化(可能还在内存里或者临时缓存)。别慌,咱们换个思路,用自定义存储类或者模型save方法重写就能完美解决这个需求,下面给你两种可行的方案:
方案一:重写模型的save方法(灵活易控)
这种方式先把视频临时存在S3(或者本地),然后在保存模型时调用Vimeo API上传,最后替换成Vimeo的链接并清理临时文件。
步骤1:安装Vimeo Python SDK
pip install vimeo
步骤2:配置Vimeo API密钥(settings.py)
# Vimeo API配置,去Vimeo开发者后台获取 VIMEO_CLIENT_ID = "你的客户端ID" VIMEO_CLIENT_SECRET = "你的客户端密钥" VIMEO_ACCESS_TOKEN = "你的访问令牌"
步骤3:编写模型逻辑
from django.db import models from django.conf import settings from django.core.files.base import ContentFile import vimeo from storages.backends.s3boto3 import S3Boto3Storage class MediaModel(models.Model): # 图片直接存S3 image = models.ImageField(storage=S3Boto3Storage(), upload_to="images/%Y/%m/%d/") # 视频先临时存S3,后续上传Vimeo后会清空 temp_video = models.FileField(upload_to="temp_videos/%Y/%m/%d/", blank=True, null=True) # 存储Vimeo的视频链接 vimeo_url = models.URLField(blank=True, null=True) def save(self, *args, **kwargs): # 只有当有新视频且还没上传到Vimeo时处理 if self.temp_video and not self.vimeo_url: # 初始化Vimeo客户端 vimeo_client = vimeo.VimeoClient( token=settings.VIMEO_ACCESS_TOKEN, key=settings.VIMEO_CLIENT_ID, secret=settings.VIMEO_CLIENT_SECRET ) try: # 读取临时视频文件内容 with self.temp_video.open("rb") as f: video_content = ContentFile(f.read()) # 上传到Vimeo(大文件可以用upload_large方法) upload_response = vimeo_client.upload( data=video_content, name=self.temp_video.name, description="从Django项目上传的视频" ) # 获取Vimeo视频的详情和链接 video_uri = upload_response.headers["Location"] video_info = vimeo_client.get(video_uri).json() self.vimeo_url = video_info["link"] # 或者用player_embed_url获取嵌入链接 # 清理临时文件和字段 self.temp_video.delete(save=False) self.temp_video = None except Exception as e: # 上传失败时可以抛出错误或者做降级处理 raise RuntimeError(f"Vimeo上传失败: {str(e)}") super().save(*args, **kwargs)
方案二:自定义Vimeo存储类(更优雅的Django风格)
直接给视频字段指定自定义存储类,让Django在处理文件上传时自动调用Vimeo API,无需临时存储。
步骤1:编写自定义存储类
from django.core.files.storage import Storage from django.conf import settings import vimeo class VimeoStorage(Storage): def _open(self, name, mode="rb"): # 不需要从Vimeo读取文件到Django,直接抛出异常 raise NotImplementedError("VimeoStorage不支持打开文件") def _save(self, name, content): # 核心:把文件内容上传到Vimeo vimeo_client = vimeo.VimeoClient( token=settings.VIMEO_ACCESS_TOKEN, key=settings.VIMEO_CLIENT_ID, secret=settings.VIMEO_CLIENT_SECRET ) upload_response = vimeo_client.upload( data=content, name=name, description="通过VimeoStorage上传的视频" ) video_uri = upload_response.headers["Location"] video_info = vimeo_client.get(video_uri).json() # 返回Vimeo的视频链接作为存储的"文件名" return video_info["link"] def exists(self, name): # 简化处理:默认不存在重复视频,每次都新上传 return False def url(self, name): # 因为name已经是Vimeo的链接,直接返回 return name
步骤2:在模型中使用
from django.db import models from storages.backends.s3boto3 import S3Boto3Storage from .storage import VimeoStorage class MediaModel(models.Model): image = models.ImageField(storage=S3Boto3Storage(), upload_to="images/%Y/%m/%d/") # 视频直接用VimeoStorage video = models.FileField(storage=VimeoStorage(), upload_to="videos/")
注意事项
- 大文件上传:如果视频超过1GB,建议用Vimeo SDK的
upload_large方法实现分块上传,避免超时。 - 错误处理:记得给API调用加
try-except块,处理网络异常、权限不足等情况。 - Vimeo权限:确保你的Access Token拥有
create和edit视频的权限,去Vimeo开发者后台配置。
内容的提问来源于stack exchange,提问作者Rohan
相关产品推荐
相关产品推荐

