Django 连接腾讯云COS

最近在写 Desert 这个音游项目,为了不占用服务器带宽,我选择使用 COS 而不是存储在服务器本地。我的设计是不开放单独的上传接口,只让管理员登陆 Admin Site 手动创建数据库 record 并上传曲目文件。这么设计的话我们就需要使 Admin Site 的文件上传组件能被我们实现,然而这样很困难,而且不符合规范。

通过观察 model.FileField() 可以发现其中可传参数 storage。该参数实现了 Storage 类,描述了文件存储方式。

通过翻阅 Storage 类的代码,可以发现它需要实现若干方法。我们的需求也就是上传腾讯云COS仅需实现save()url()generate_filename()。库中源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
def save(self, name, content, max_length=None):
"""
Save new content to the file specified by name. The content should be
a proper File object or any Python file-like object, ready to be read
from the beginning.
"""
# Get the proper name for the file, as it will actually be saved.
if name is None:
name = content.name
if not hasattr(content, "chunks"):
content = File(content, name)
name = self.get_available_name(name, max_length=max_length)
name = self._save(name, content)
# Ensure that the name returned from the storage system is still valid.
validate_file_name(name, allow_relative_path=True)
return name

def generate_filename(self, filename):
"""
Validate the filename by calling get_valid_name() and return a filename
to be passed to the save() method.
"""
filename = str(filename).replace("\\", "/")
# `filename` may include a path as returned by FileField.upload_to.
dirname, filename = os.path.split(filename)
if ".." in pathlib.PurePath(dirname).parts:
raise SuspiciousFileOperation(
"Detected path traversal attempt in '%s'" % dirname
)
return os.path.normpath(os.path.join(dirname, self.get_valid_name(filename)))


def url(self, name):
"""
Return an absolute URL where the file's contents can be accessed
directly by a web browser.
"""
raise NotImplementedError("subclasses of Storage must provide a url() method")

在我们的需求中,save() 所做的是将文件上传到腾讯云 COS 上,generate_filename() 需要生成文件名,url() 则返回一个文件直链,供 302 跳转。实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
secret_id = settings.COS_SECRET_ID # secret_id
secret_key = settings.COS_SECRET_KEY # secret_key
region = settings.REGION # bucket region
bucket = settings.BUCKET # bucket name
config = CosConfig(Region=region, Secret_id=secret_id, Secret_key=secret_key) # build a config
client = CosS3Client(config) # get a client object
host = 'https://' + bucket + '.cos.' + region + '.myqcloud.com/'
'''
以上的部分字段需自行写入 setting.py 中
'''


@deconstructible
class TencentStorage(Storage):
def save(self, name, content, max_length=None):
client.put_object(
Bucket=bucket,
Key='song/' + name,
Body=content.read()
)
return name

# 因为没有修改文件名的需求所以不实现,有需要的可以自行在这里构造字符串
def generate_filename(self, filename):
pass

def url(self, name):
file_url = client.get_object_url(bucket, 'song/' + name)
return str(file_url)

有关腾讯云 COS SDK 的 API 使用可以自行阅读文档,这里不做解释。

实现好 Storage 后,只需传入 model.FileField 即可。

1
song_file = models.FileField(storage=TencentStorage(), null=True)

然后登陆 Admin Site,编辑对象即可上传。成品图:

image-20220329110141847

作者

EvanLuo42

发布于

2022-03-29

更新于

2022-03-29

许可协议

评论

+ +