使用 Python 上传文件到 DigitalOcean 云存储空间

DigitalOcean Spaces 拥有高达 250G 的云存储容量,并且支持单向(出站)高达1TB的月流量,入站流量不限制。特别适合个人用于文件存储及备份。

为了提高文件的上传效率,方便管理,学会使用 Python 上传文件到 DigitalOcean 空间绝对是个不错的选择。今天我们就来谈谈如何通过 Python 上传文件到 DigitalOcean 云存储空间的具体方法。在本文中,我们将通过 Pyhon 工具 Boto3 来达成此目标。

首先,我们打开终端通过以下命令安装 Boto3 库:

pip install boto3

上传本地文件到 DigitalOcean Spaces

安装完 Boto3 后,我们便可以使用其中的 upload_file 方法来最终完成文件上传功能。另外,我们将使用 mimetypes 来完成文件格式的转换。

新建一个名为 digitalocean.py 的 Pyhon 文件,代码如下:

import boto3
import mimetypes


def get_spaces_client(**kwargs):
    """
    :param kwargs:
    :return:
    """
    region_name = kwargs.get("region_name")
    endpoint_url = kwargs.get("endpoint_url")
    key_id = kwargs.get("key_id")
    secret_access_key = kwargs.get("secret_access_key")

    session = boto3.session.Session()

    return session.client(
        's3',
        region_name=region_name,
        endpoint_url=endpoint_url,
        aws_access_key_id=key_id,
        aws_secret_access_key=secret_access_key
    )


def upload_file_to_space(spaces_client, space_name, file_src, save_as, **kwargs):
    """
    :param spaces_client: Your DigitalOcean Spaces client from get_spaces_client()
    :param space_name: Unique name of your space. Can be found at your digitalocean panel
    :param file_src: File location on your disk
    :param save_as: Where to save your file in the space
    :param kwargs
    :return:
    """

    is_public = kwargs.get("is_public", False)
    content_type = kwargs.get("content_type")
    meta = kwargs.get("meta")

    if not content_type:
        file_type_guess = mimetypes.guess_type(file_src)

        if not file_type_guess[0]:
            raise Exception("We can't identify content type. Please specify directly via content_type arg.")

        content_type = file_type_guess[0]

    extra_args = {
        'ACL': "public-read" if is_public else "private",
        'ContentType': content_type
    }

    if isinstance(meta, dict):
        extra_args["Metadata"] = meta

    return spaces_client.upload_file(
        file_src,
        space_name,
        save_as,

        # boto3.s3.transfer.S3Transfer.ALLOWED_UPLOAD_ARGS
        ExtraArgs=extra_args
    )

现在,您可以随时在任何 Python 模块中引用上述文件来上传本地文件到 DigitalOcean 云存储空间。

您可以这么做:

import digitalocean


client = digitalocean.get_spaces_client(
    region_name="nyc3",
    endpoint_url="https://nyc3.digitaloceanspaces.com",
    key_id="my-spaces-key",
    secret_access_key="my-spaces-secret-access-key"
)

digitalocean.upload_file_to_space(client, "space-name", "local/my_photo.jpg", "place/here/my_photo.jpg")

注意:请使用您自己的 “Spaces access keys” 来替换上述代码中 key_idsecret_access_key 的值。您可以在 “DigitalOcean Spaces” 控制面板 “API” 菜单中的 “Spaces access keys” 来查询或者生成访问密钥。

如何设置 DigitalOcean 云存储空间文件权限为公开?

上传到 DigitalOcean Spaces 的文件默认权限为私有,只有您自己可以查看。如果您想通过 URL 将文件分享给其它人,必须设置文件权限为公开。我们可以通过设置 is_public=True 来做到这点。

虽然 Boto3 支持完整的 ACL 权限设置,但 DigitalOcean 云存储只支持 public-readprivate 两种权限。所以,我们只需要简单的通过设置 is_public=True 来完成权限的设置。

digitalocean.upload_file_to_space(
    client,
    "space-name",
    "local/my_photo.jpg",
    "place/here/my_photo.jpg",
    is_public=True
)

与文件格式 “binary/octet-stream” 有关的错误

如果您曾使用过 Boto3,一定遇到过一个 mime-type 错误。事实上,无论是 Amazon S3 还是 DigitalOcean 云存储都存在使用 binary/octet-streamoctet/binary 相关的 mime-type 错误。在这里,我们通过 mimetypes.guess_type 解决了这个问题。当然,也可以通过 content_type 参数直接完成。

digitalocean.upload_file_to_space(
    client,
    "space-name",
    "local/my_styles.css",
    "place/my_styles.css",
    content_type="text/css"
)

设置文件元数据(Metadata)

digitalocean.upload_file_to_space(
    client,
    "space-name",
    "local/my_styles.css",
    "space/path/my_styles.css",
    meta={
      "x-amz-meta-my-key": "your-value"
    }
)

使用 “put_object” 上传字节数组文件

首先,添加以下函数到 digitalocean.py 模块:

def upload_bytes_array_to_space(spaces_client, space_name, file_body, save_as, **kwargs):
    """
    :param spaces_client: Your DigitalOcean Spaces client from get_spaces_client()
    :param space_name: Unique name of your space. Can be found at your digitalocean panel
    :param file_body: Byte Array File
    :param save_as: Where to save your file in the space
    :param kwargs:
    :return:
    """

    is_public = kwargs.get("is_public", False)
    content_type = kwargs.get("content_type")
    meta = kwargs.get("meta")

    args = {
        "Bucket": space_name,
        "Body": file_body,
        "Key": save_as,
        "ACL": "public-read" if is_public else "private"
    }

    if content_type:
        args["ContentType"] = content_type

    if isinstance(meta, dict):
        args["Metadata"] = meta

    return spaces_client.put_object(**args)

接下来,您就可以通过此函数通过二进制模式上传文件到 DigitalOcean 云存储空间。

with open("scripts/test/test.jpg", "rb") as my_file:
    digitalocean.upload_bytes_array_to_space(
        client,
        "space-name",
        my_file,
        "space/path/test6.jpg",
        is_public=True
    )

当然,您也可以根据需要在上述代码中指定 content_type

设置文件元数据

与之前一样,您可以通过指定 meta 参数来为文件添加额外的 “metadata” 元数据信息。

with open("scripts/test/test.jpg", "rb") as my_file:
    digitalocean.upload_bytes_array_to_space(
        client,
        "space-name",
        my_file,
        "space/path/test6.jpg",
        is_public=True,
        meta={
            "x-amz-meta-my-key": "your-value"
        }
    )

总结

目前 DigitalOcean Spaces 正在推行免费试用2个月的优惠活动,您可以随时取消试用,并且如果试用期额度没有用完,还可以再次启用。试用期结束后,您需要支付5美元一个月的费用。

DigitalOcean 云存储空间兼容 AWS S3 存储标准,所以 DigitalOcean Spaces 的扩展应用很丰富。除了本文提供的方法,您还可以使用 Cyberduck 等客户端工具将数据同步备份到DigitalOcean 云存储空间。

最后,位于中国的用户需要注意,DigitalOcean 云存储空间纽约区网速要高于新加坡。