1. 前言
本篇文章将讲述如何利用腾讯云对象存储的 python SDK 上传图片,包含基础配置,原理概述和前后端搭建。
文章内容总共分为四个部分,一为环境准备,如何搭建云对象存储基于 python 的 SDK 环境;二为编写后台,如何调用 SDK 中的上传图片函数,并对图像进行进一步的处理;三为前端交互,如何利用 ajax 封装 formData 并提交至后台;最后对结果进行测试。
2. 环境准备
- 首先进入腾讯云控制台,进入对象存储管理中心,点击存储桶-> 创建存储桶,填写名称和编辑访问权限。如果是搭建图床,权限可以设置为共有读私有写,因为图床一般需要给网站浏览者访问,但只有管理员能上传图片。
- 创建完成以后,点击已经创建的存储桶,然后进入权限管理。
- Policy 权限设置中删除默认的策略,然后添加策略,设置用户为所有用户,操作为所有操作,不设置可能会导致之后上传图片报 403 错误,然后保存即可。
- 由于使用了腾讯云对象存储的 Python SDK,所以我们需要利用 pip 安装所需的第三方库。在命令行中输入
pip install -U cos-python-sdk-v5
安装 SDK,到此环境配置就结束了。
3. 编写后台
初始化配置信息
- 我在 Django 项目中添加了一个 App,名称为 img_cos,在该 App 中新建了一个 config.py 文件,在其中写入了关于腾讯云存储的相关配置信息。
# appid 已在配置中移除,请在参数 Bucket 中带上 appid。Bucket 由 BucketName-APPID 组成
# 1. 设置用户配置, 包括 secretId,secretKey 以及 Region
# -*- coding=utf-8
import logging
import os
import sys
import uuid
from PIL import Image
from qcloud_cos import CosConfig, CosServiceError
from qcloud_cos import CosS3Client
from my_blog import settings
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
secret_id = '******' # 替换为用户的 secretId
secret_key = '******' # 替换为用户的 secretKey
region = 'ap-shanghai' # 替换为用户的 Region
token = None # 使用临时密钥需要传入 Token,默认为空,可不填
scheme = 'https' # 指定使用 http/https 协议来访问 COS,默认为 https,可不填
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token, Scheme=scheme)
# 2. 获取客户端对象
client = CosS3Client(config)
host = 'https://zyk-1300089264.cos.ap-shanghai.myqcloud.com' # 存储桶访问地址
folder_path = '/article/' # 存储桶中保存图片文件夹名
- 注意:secret_id, secret_key 和 region 都需要替换为自己存储桶对应的信息。secret_id 和 secret_key 可以到对象存储控制台的密钥管理获取,这里采用的是项目管理中的 secret_id 和 secret_key。
上传图片
- 配置完毕之后,开始编写上传图片函数,在这里调用的 SDK 中的断点续传图片,支持断点续传,安全性更高。
- 上传原理为:将上传的图片暂时保存至本地服务器,调用 SDK 中的上传图片方法(捕捉异常),若上传成功,则删除本地服务器上的图片,并返回图片在对象存储中的访问地址。
- 由于图片名称可能会重复,因此采用 UUID 生成图片名称,保证名称唯一性。
为了节约空间,调用 Pillow(没有安装 Pillow 库的小伙伴,可以先用pip
命令安装 Pillow)中thumbnail
方法生成缩略图。具体代码如下。
# appid 已在配置中移除,请在参数 Bucket 中带上 appid。Bucket 由 BucketName-APPID 组成
# 1. 设置用户配置, 包括 secretId,secretKey 以及 Region
# -*- coding=utf-8
import logging
import os
import sys
import uuid
from PIL import Image
from qcloud_cos import CosConfig, CosServiceError
from qcloud_cos import CosS3Client
from my_blog import settings
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
secret_id = '******' # 替换为用户的 secretId
secret_key = '******' # 替换为用户的 secretKey
region = 'ap-shanghai' # 替换为用户的 Region
token = None # 使用临时密钥需要传入 Token,默认为空,可不填
scheme = 'https' # 指定使用 http/https 协议来访问 COS,默认为 https,可不填
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token, Scheme=scheme)
# 2. 获取客户端对象
client = CosS3Client(config)
host = 'https://zyk-1300089264.cos.ap-shanghai.myqcloud.com' # 存储桶访问地址
folder_path = '/article/' # 存储桶中保存图片文件夹名
# 上传图片
def upload_file_senior(file):
file_name = create_file_name(file) # 根据UUID生成文件名
root_path = os.path.join(settings.MEDIA_ROOT, 'vditor') # 生成文件上传目录
if not os.path.exists(root_path): # 判断文件上传目录是否存在
os.makedirs(root_path) # 不存在,则创建
file_path = os.path.join(root_path, file_name)
with open(file_path, 'wb') as f: # 文件流将图片写入本地
for c in file.chunks():
f.write(c)
file_path = compress_img(file_path, 0.75) # 压缩图片,生成缩略图
try: # 捕捉异常
response = client.upload_file( # 根据文件大小自动选择简单上传或分块上传,分块上传具备断点续传功能。
Bucket='zyk-1300089264', # 存储桶名称
LocalFilePath=file_path, # 本地图片路径
Key=folder_path + file_name, # 上传路径
PartSize=1,
MAXThread=10,
EnableMD5=False
)
if response['ETag'] != "":
os.remove(file_path) # 上传成功,删除本地图片
return host + folder_path + file_name
except CosServiceError as e:
print(e.get_digest_msg())
return None
# 利用UUID生成文件名,防止重名
def create_file_name(file):
type_name = file.name[file.name.index('.'):] # 获取文件后缀
file_name = '{}{}'.format(uuid.uuid4(), type_name) # 生成文件名
return file_name
# 压缩图片 (file_path为图片路径,rate为压缩率,压缩率范围为0~1)
def compress_img(file_path, rate):
image = Image.open(file_path) # 获得图像
width = int(image.width * rate) # 宽
height = int(image.height * rate) # 高
image.thumbnail((width, height), Image.ANTIALIAS) # 生成缩略图
image.save(file_path) # 保存
return file_path
- 编写视图函数,在 img_cos/views.py 中加入图片上传函数(在这里利用了
djangorestframework 库,未安装的小伙伴可以通过pip install djangorestframework
命令安装此库)。先从请求中获取图片,然后调用之前封装好的 SDK 上传图片方法,根据上传成功与否,封装 json 数据返回至前端,上传成功则将图片访问 URL 同时返回至前端,代码如下。
from rest_framework.decorators import api_view
from rest_framework.response import Response
from img_cos.config import upload_file, upload_file_senior
"""
上传图片至腾讯云COS
"""
@api_view(['POST']) # 只允许POST请求
def upload_to_cos(request):
file = request.FILES.get('smfile') # 获取上传图片
url = upload_file_senior(file) # 调用上传图片SDK,返回图片访问URL
if url is None: # 上传失败
return Response({"msg": "上传失败", "code": 0})
return Response({"msg": "上传成功", "code": 1, "url": url}) # 上传成功
- 将视图函数注册到 img_cos/urls.py 中,如下。
from django.urls import path
from img_cos import views
urlpatterns = [
path('upload_to_cos', views.upload_to_cos, name='upload_to_cos'), # 上传图片至腾讯云COS
]
4. 前端交互
- 获取上传图片文件对象,利用 append()方法将其封装成 FormData,调用 ajax 上传至后台。在这里需要注意 ajax 的编写,需要添加不处理上传数据的参数。
- 具体思路:选择要上传的图片,然后设置一个上传图片按钮,为该按钮添加点击事件,事件中调用上传图片函数,用户点击此按钮即可实现图片上传。
- 在我的博客项目中,文章表单字段较多,而文章数据表的图片字段只保存图片访问 URL。因此,根据 ajax 上传图片返回的数据,若上传成功,将返回的图片访问 URL 写入一个隐藏的
<input>
标签,然后再提交表单。具体实现代码如下:
//上传文章贴图
function uploadArticleImg() {
if ($('#img').val() === "" || $('#img').val() == null) { // 判断文件是否为空
layer.msg("请选择要上传的图片");
return;
}
let formData = new FormData();
formData.append('smfile', $('#img')[0].files[0]); //将文件对象写入formData
$.ajax({
type: 'POST',
url: '{% url 'upload_to_cos' %}',
headers: {'X-CSRFToken': getCsrfToken()},
data: formData,
dataType: 'JSON',
contentType: false, //不处理数据
processData: false,
cache: false,
success: function (data) {
if (data.code === 1) {
layer.msg(data.msg);
$("#link_url").val(data.url); // 将图片访问URL写入隐藏的input标签中
} else {
layer.msg(data.msg);
}
},
error: function (data) {
console.log(data);
}
});
}
5. 结果测试
- 运行 Django 项目,测试图片上传功能,选择要上传的图片,点击上传图片按钮,观察结果。
- 进入腾讯云控制台,查看存储桶中是否生成刚刚上传的图片。
- 注意:如果想指定网站访问对象存储中的图片,需要在存储桶中的基础配置-> 跨域访问 CORS 设置添加自己网站的域名。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于