跳转至

腾讯云对象存储

Django 中的使用方式

在 Django 的 models.py 中使用 models.URLField 来存储文件在腾讯云对象存储中的链接。

新建存储桶

打开 https://console.cloud.tencent.com/cos/bucket 点击「创建存储桶」:

第一页基本信息注意选择公有读私有写,这样资源可以直接在互联网中读访问。第二页高级可选配置不需要动。第三页确认配置点击「创建」:

之后就可以使用这个存储桶了。

新建访问账号

账号(SecretId 和 SecretKey)有两个作用,一个是可以开发者调用腾讯云对象存储 API 来上传文件,另一个是助教可以通过 COSBrowser 来修改文件内容,助教也可以在 COSBrowser 中创建新的文件在 Django 后端修改文件链接。

新建策略

查阅 https://cloud.tencent.com/document/product/436/31923,我们需要先创建一个策略。在 https://console.cloud.tencent.com/cam/policy 中点击「新建自定义策略」,「按策略生成器创建」。做下面的配置,确保这个策略仅能够访问到这一个存储桶:

命名为 policy-cos-access-<bucket-name> 表示这个策略可以访问到这个存储桶。点击「完成」。

新建账号

https://console.cloud.tencent.com/cam 的「用户列表」中,点击「新建用户」,「快速创建」,按照如下选择:

接着添加两个账户,注意开启编程访问。这里创建了两个账户,dev 结尾为开发者使用,ta 结尾为助教使用:

策略选择我们刚刚创建的策略,记得移除管理权限:

点击创建用户。拿到 SecretId 和 SecretKey,与用户名一起给到开发者与助教即可:

助教使用 COSBrowser

为了让助教可以在线修改文件,直接使用腾讯云对象存储做好的 COSBrowser,使用网页版就可以了,登录链接是 https://cosbrowser.cloud.tencent.com/login

打开是一个登录界面,账号密码需要管理员提供(上面的 SecretId 和 SecretKey):

有两种界面,我们切换到编辑模式:

在编辑模式可以直接对文件内容进行修改,或者创建新文件:

新创建的文件可以右键点击分享,拿到文件链接:

开发者调用 API

https://cloud.tencent.com/document/product/436/7751 中有腾讯云对象存储提供的全部 API,同时提供了对应的 SDK https://cloud.tencent.com/document/product/436/6474

这里为了简洁,我使用纯 Python 代码实现了文本文件的上传功能:

Python
 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class TencentCloudObjectStorage:
    def __init__(self):
        self.COS_SECRET_ID = os.getenv("COS_SECRET_ID")
        self.COS_SECRET_KEY = os.getenv("COS_SECRET_KEY")
        BUCKET_NAME = os.getenv("BUCKET_NAME")
        BUCKET_REGION = os.getenv("BUCKET_REGION")
        self.HOST = f"{BUCKET_NAME}.cos.{BUCKET_REGION}.myqcloud.com"

    def GetAuthorization(
        self,
        file_name: str,
        headers: dict,
    ) -> str:
        def UrlEncode(string: str) -> str:
            special_characters = " !\"#$%&'()*+,/:;<=>?@[\\]^`{|}"
            data = string.encode()
            data = b"".join(
                [
                    byte.to_bytes()
                    if chr(byte) not in special_characters
                    else f"%{byte:02X}".encode()
                    for byte in data
                ]
            )
            return data.decode()

        sign_headers = dict(
            [(UrlEncode(k).lower(), UrlEncode(v)) for k, v in headers.items()]
        )

        current_seconds = int(time.time())
        authorization_sign_time = f"{current_seconds-60};{current_seconds+60}"
        authorization_headers = ";".join(sorted(sign_headers.keys()))

        sign_key = hmac.new(
            self.COS_SECRET_KEY.encode(),
            authorization_sign_time.encode(),
            hashlib.sha1,
        ).hexdigest()

        http_headers = "&".join([f"{t[0]}={t[1]}" for t in sign_headers.items()])
        http_string = f"put\n/{file_name}\n\n{http_headers}\n"
        http_string_sha1 = hashlib.sha1(http_string.encode()).hexdigest()
        string_to_sign = f"sha1\n{authorization_sign_time}\n{http_string_sha1}\n"

        authorization_sign = hmac.new(
            sign_key.encode(), string_to_sign.encode(), hashlib.sha1
        ).hexdigest()

        authorization = f"q-sign-algorithm=sha1&q-ak={self.COS_SECRET_ID}&q-sign-time={authorization_sign_time}&q-key-time={authorization_sign_time}&q-header-list={authorization_headers}&q-url-param-list=&q-signature={authorization_sign}"
        return authorization

    def UploadPlainTextFile(self, file_content: str, suffix: str = "txt") -> str | None:
        file_name = (
            datetime.datetime.now().strftime("%y%m%d%H%M")
            + "-"
            + uuid.uuid4().hex
            + f".{suffix}"
        )
        url = f"https://{self.HOST}/{file_name}"

        file_data = file_content.encode()
        headers = {"Content-Type": "text/plain"}
        headers["Authorization"] = self.GetAuthorization(
            file_name=file_name, headers=headers
        )

        response = requests.put(url=url, data=file_data, headers=headers)

        print(response.status_code)
        print(response.content.decode())
        if response.status_code == 200:
            return url
        else:
            return None

这里 COS_SECRET_ID 和 COS_SECRET_KEY 都使用环境变量,需要在程序运行前设置好。