15-day09-项目开发-wiki本地图片上传oss(python实现oss上传文件、python创建oss桶、wiki项目创建桶、wiki上传图片)

一、python实现oss上传文件

  • 第一步 :开通腾讯云COS,开通后创建桶;

image
image

  • 第二步 :查看腾讯云 COS 提供的 python SDK

Python OSS SDK :https://cloud.tencent.com/document/product/436/12269

image

  • 第三步 :使用SDK 实现COS文章上传
  • 初始化
# 使用 pip 安装(推荐)sdk
pip install -U cos-python-sdk-v5
# 初始化 - 请参考以下示例代码:

# -*- coding=utf-8
# appid 已在配置中移除,请在参数 Bucket 中带上 appid。Bucket 由 BucketName-APPID 组成
# 1. 设置用户配置, 包括 secretId,secretKey 以及 Region
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
import sys
import logging
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
secret_id = 'SECRETID'      # 替换为用户的 secretId(登录访问管理控制台获取)
secret_key = 'SECRETKEY'      # 替换为用户的 secretKey(登录访问管理控制台获取)
region = 'COS_REGION'     # 替换为用户的 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)
# 参照下文的描述。或者参照 Demo 程序,详见 https://github.com/tencentyun/cos-python-sdk-v5/blob/master/qcloud_cos/demo.py
  • 创建和查询桶
# 创建存储桶
response = client.create_bucket(
   Bucket='examplebucket-1250000000'
)

# 查询存储桶列表
response = client.list_buckets(
)
  • 上传文件

#### 高级上传接口(推荐)
# 根据文件大小自动选择简单上传或分块上传,分块上传具备断点续传功能。
response = client.upload_file(
   Bucket='examplebucket-1250000000',
   LocalFilePath='local.txt',	# 本地上传文件路径
   Key='picture.jpg',			# 上传到桶之后的文件名
   PartSize=1,
   MAXThread=10,
   EnableMD5=False
)
print(response['ETag'])
  • 创建 secret_idsecret_key

image

  • python脚本上传oss文件测试脚本
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client

secret_id = 'AKIDoSpwmLdfQu85lAJAk35rMQDjywd3C4fz'      # 替换为用户的 secretId(登录访问管理控制台获取)
secret_key = '?'      # 替换为用户的 secretKey(登录访问管理控制台获取)
region = 'ap-beijing'     # 替换为用户的 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)
# 参照下文的描述。或者参照 Demo 程序,详见 https://github.com/tencentyun/cos-python-sdk-v5/blob/master/qcloud_cos/demo.py


# 根据文件大小自动选择简单上传或分块上传,分块上传具备断点续传功能。
response = client.upload_file(
   Bucket='bucket-1258023638',
   LocalFilePath='./test.jpg',	# 本地上传文件路径
   Key='test.jpg',			# 上传到桶之后的文件名
)
print(response['ETag'])

image

二、python创建oss桶(创建项目时创建桶)

  • 第一步 :优化项目表,增加桶字段、桶区域字段
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/models.py 

class Project(models.Model):
    """ 项目表 """
    COLOR_CHOICES = (
        (1, "#56b8eb"),  # 56b8eb
        (2, "#f28033"),  # f28033
        (3, "#ebc656"),  # ebc656
        (4, "#a2d148"),  # a2d148
        (5, "#20BFA4"),  # #20BFA4
        (6, "#7461c2"),  # 7461c2,
        (7, "#20bfa3"),  # 20bfa3,
    )

    name = models.CharField(verbose_name='项目名', max_length=32)
    color = models.SmallIntegerField(verbose_name='颜色', choices=COLOR_CHOICES, default=1)
    desc = models.CharField(verbose_name='项目描述', max_length=255, null=True, blank=True)

    use_space = models.BigIntegerField(verbose_name='项目已使用空间', default=0, help_text='字节')

    star = models.BooleanField(verbose_name='星标', default=False)

    join_count = models.SmallIntegerField(verbose_name='参与人数', default=1)
    creator = models.ForeignKey(verbose_name='创建者', to='UserInfo', on_delete=models.CASCADE)
    create_datetime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)

    bucket = models.CharField(verbose_name='cos桶', max_length=128)
    region = models.CharField(verbose_name='cos区域', max_length=32)

    # 查询:可以省事;
    # 增加、删除、修改:无法完成
    # project_user = models.ManyToManyField(to='UserInfo',through="ProjectUser",through_fields=('project','user'))
  • 第二步 :将操作cos用到的secret_idsecret_key此类种重要信息放置在 loca_settings.py
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat Bug_manager/local_settings.py 

######## LANGUAGE #######
LANGUAGE_CODE = 'zh-hans'

######## sms ########
# 腾讯云短信应用 app_id
TENCENT_SMS_APP_ID = 1400538340
# 腾讯云短信应用 app_key
TENCENT_SMS_APP_KEY = "?"
# 腾讯云短信签名内容
TENCENT_SMS_SIGN = "SRE运维充电站"

######## COS ########
TENCENT_COS_ID = 'AKIDoSpwmLdfQu85lAJAk35rMQDjywd3C4fz'  # 替换为用户的 secretId(登录访问管理控制台获取)
TENCENT_COS_KEY = '?'  # 替换为用户的 secretKey(登录访问管理控制台获取)

######## Redis ########
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379", # 安装redis的主机的 IP 和 端口
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            # 连接池
            "CONNECTION_POOL_KWARGS": {
                "max_connections": 1000,
                "encoding": 'utf-8'
            },
            # "PASSWORD": "foobared" # redis密码
        }
    },
    # "master": {
    #     "BACKEND": "django_redis.cache.RedisCache",
    #     "LOCATION": "redis://127.0.0.1:6379",  # 安装redis的主机的 IP 和 端口
    #     "OPTIONS": {
    #         "CLIENT_CLASS": "django_redis.client.DefaultClient",
    #         # 连接池
    #         "CONNECTION_POOL_KWARGS": {
    #             "max_connections": 1000,
    #             "encoding": 'utf-8'
    #         },
    #         # "PASSWORD": "foobared" # redis密码
    #     }
    # }
}
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat Bug_manager/settings.py 
...
...
######## sms ########
# 腾讯云短信应用 app_id
TENCENT_SMS_APP_ID = 140025383401
# 腾讯云短信应用 app_key
TENCENT_SMS_APP_KEY = "?"
# 腾讯云短信签名内容
TENCENT_SMS_SIGN = "SRE运维充电站"

######## COS ########
TENCENT_COS_ID = 'secretId'  # 替换为用户的 secretId(登录访问管理控制台获取)
TENCENT_COS_KEY = 'secretKey'  # 替换为用户的 secretKey(登录访问管理控制台获取)
...
...
  • 第三步 :为操作cos的操作封装一个工具
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat utils/tencent/cos.py          

from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
from django.conf import settings

def create_bucket(bucket, region='ap-beijing'):
    # region = 'ap-beijing'  # 替换为用户的 Region (桶的区域)
    token = None  # 使用临时密钥需要传入 Token,默认为空,可不填
    scheme = 'https'  # 指定使用 http/https 协议来访问 COS,默认为 https,可不填

    config = CosConfig(Region=region, SecretId=settings.TENCENT_COS_ID, SecretKey=settings.TENCENT_COS_KEY, Token=token, Scheme=scheme)
    # 2. 获取客户端对象
    client = CosS3Client(config)
    # 参照下文的描述。或者参照 Demo 程序,详见 https://github.com/tencentyun/cos-python-sdk-v5/blob/master/qcloud_cos/demo.py

    # 创建存储桶
    client.create_bucket(
        Bucket=bucket,
        ACL="public-read"  # private  /  public-read / public-read-write
    )
  • 第四步 :优化创建项目的 视图,增加创建项目时,会创建项目对应的桶的操作;
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/views/project.py      


from django.shortcuts import render, redirect
from web_app.forms.project import ProjectModelForm
from django.http import JsonResponse, HttpResponse
from web_app import models
from utils.tencent.cos import create_bucket
import time

def project_list(request):
    """ 项目列表 """
    if request.method == 'GET':
        # GET请求查看项目列表
        """
        1. 从数据库中获取两部分数据
            我创建的所有项目:已星标、未星标
            我参与的所有项目:已星标、未星标
        2. 提取已星标
            列表 = 循环 [我创建的所有项目] + [我参与的所有项目] 把已星标的数据提取

        得到三个列表:星标、创建、参与
        """
        project_dict = {'star': [], 'my': [], 'join': []}

        my_project_list = models.Project.objects.filter(creator=request.bug_manager.user)
        for row in my_project_list:
            if row.star:
                project_dict['star'].append({"value": row, 'type': 'my'})
            else:
                project_dict['my'].append(row)

        join_project_list = models.ProjectUser.objects.filter(user=request.bug_manager.user)
        for item in join_project_list:
            if item.star:
                project_dict['star'].append({"value": item.project, 'type': 'join'})
            else:
                project_dict['join'].append(item.project)

        form = ProjectModelForm(request)
        return render(request, 'project_list.html', {'form': form, 'project_dict': project_dict})

    # POST
    form = ProjectModelForm(request=request, data=request.POST)
    if form.is_valid():
        # 为项目创建桶
        bucket = "{}-{}-1258023638".format(request.bug_manager.user.mobile_phone, str(int(time.time())*1000))
        region = 'ap-beijing'
        create_bucket(bucket, region)

        form.instance.bucket = bucket
        form.instance.region = region

        # 通过验证,创建项目 :用户仅填写了项目名称、颜色、描述 ,查看设计的项目表,只有创建者没有默认值,所以获取创建人再写入数据库,不然会报错
        form.instance.creator = request.bug_manager.user    # 获取创建者
        # print(form.instance.creator)
        form.save()
        return JsonResponse({'status': True})
    return JsonResponse({'status': False, 'error': form.errors})
  • 第五步 :创建一个新项目,测试是是否会自动创建桶

image
image

  • 第六步 :优化创建项目时桶的名称,form.cleaned_data 为 form表单提交数据,从中可以获取项目名称 ,增加项目名称
from django.shortcuts import render, redirect
from web_app.forms.project import ProjectModelForm
from django.http import JsonResponse, HttpResponse
from web_app import models
from utils.tencent.cos import create_bucket
import time

def project_list(request):
    """ 项目列表 """
    if request.method == 'GET':
        # GET请求查看项目列表
        """
        1. 从数据库中获取两部分数据
            我创建的所有项目:已星标、未星标
            我参与的所有项目:已星标、未星标
        2. 提取已星标
            列表 = 循环 [我创建的所有项目] + [我参与的所有项目] 把已星标的数据提取

        得到三个列表:星标、创建、参与
        """
        project_dict = {'star': [], 'my': [], 'join': []}

        my_project_list = models.Project.objects.filter(creator=request.bug_manager.user)
        for row in my_project_list:
            if row.star:
                project_dict['star'].append({"value": row, 'type': 'my'})
            else:
                project_dict['my'].append(row)

        join_project_list = models.ProjectUser.objects.filter(user=request.bug_manager.user)
        for item in join_project_list:
            if item.star:
                project_dict['star'].append({"value": item.project, 'type': 'join'})
            else:
                project_dict['join'].append(item.project)

        form = ProjectModelForm(request)
        return render(request, 'project_list.html', {'form': form, 'project_dict': project_dict})

    # POST
    form = ProjectModelForm(request=request, data=request.POST)
    if form.is_valid():
        # form.cleaned_data 为 form表单提交数据,从中可以获取项目名称
        project_name = form.cleaned_data['name']
        # 为项目创建桶
        bucket = "{}-{}-{}-1258023638".format(project_name, request.bug_manager.user.mobile_phone, str(int(time.time())*1000))
        region = 'ap-beijing'
        create_bucket(bucket, region)

        form.instance.bucket = bucket
        form.instance.region = region

        # 通过验证,创建项目 :用户仅填写了项目名称、颜色、描述 ,查看设计的项目表,只有创建者没有默认值,所以获取创建人再写入数据库,不然会报错
        form.instance.creator = request.bug_manager.user    # 获取创建者
        # print(form.instance.creator)
        form.save()
        return JsonResponse({'status': True})
    return JsonResponse({'status': False, 'error': form.errors})

image

三、wiki项目创建桶(markdown上传图片到cos)


  • 第一步 :Url 设计wiki 图片上传的路由
        url(r'^wiki/upload/$', wiki.wiki_upload, name='wiki_upload'),
  • 第二步 :views 编写wiki上传图片的视图
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/views/wiki.py 

...
...
def wiki_upload(request, project_id):
    """ markdown插件上传图片 """
    print('收到图片了')
    return JsonResponse({})
  • 第三步 :调整 wiki_form.html 模板 markdow 开启图片上传
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat web_app/templates/wiki_form.html 

{% block js %}
    <script src="{% static 'plugin/editor-md/editormd.min.js' %}"></script>
    <script>

        // http://127.0.0.1:8002/manage/2/wiki/detail/
        var WIKI_DETAIL_URL = "{% url 'wiki' project_id=request.bug_manager.project.id %}";
        // markdown上传图片
        var WIKI_UPLOAD_URL = "{% url 'wiki_upload' project_id=request.bug_manager.project.id %}";

        $(function () {

            initCatalog();
            initEditorMd();
        });

        /*
        初始化markdown编辑器(textare转换为编辑器)
         */
        function initEditorMd() {
            editormd('editor', {
                placeholder: "请输入内容",
                height: 500,
                path: "{% static 'plugin/editor-md/lib/' %}",
                {#开启本地上传配置#}
                imageUpload:true,
                {#支持上传的图片类型#}
                imageFormats:["jpg",'jpeg','png','gif'],
                {#上传后的地址#}
                imageUploadURL:WIKI_UPLOAD_URL,
            })
        }


        function initCatalog() {
            $.ajax({
                url: "{% url 'wiki_catalog' project_id=request.bug_manager.project.id %}",
                type: "GET",
                dataType: "JSON",
                success: function (res) {
                    if (res.status) {
                        $.each(res.data, function (index, item) {
                            var href = WIKI_DETAIL_URL + "?wiki_id=" + item.id;
                            var li = $("<li>").attr('id', "id_" + item.id).append($('<a>').text(item.title).attr('href', href)).append($('<ul>'));

                            if (!item.parent_id) {
                                // 添加到catalog中
                                $('#catalog').append(li);
                            } else {
                                $("#id_" + item.parent_id).children('ul').append(li);
                            }
                        })
                    } else {
                        alert("初始化目录失败");
                    }
                }
            })
        }


    </script>
{% endblock %}
  • 第四步 :上传图片测试

image

# 后台报错

[08/Aug/2021 07:08:29] "GET /static/plugin/editor-md/plugins/image-dialog/image-dialog.js HTTP/1.1" 200 9125
Forbidden (CSRF token missing or incorrect.): /manage/9/wiki/upload/
[08/Aug/2021 07:08:36] "POST /manage/9/wiki/upload/?guid=1628406509906 HTTP/1.1" 403 2514
  • 第五步 :继续编辑 views 上传视图
    • 由于编辑器 post 发送图片时为携带 CSRF_TOKEN 所以,我们也没必要去源码中处理了,直接在视图中忽略csrf装饰器
@csrf_exempt
def wiki_upload(request, project_id):
    """ markdown插件上传图片 """
    print('收到图片了')
    print(request.FILES)	# 图片
    return JsonResponse({})

image

  • 第六步 :将上传到cos桶的操作封装到工具中
(Bug_manager) daizhe@daizhedeMacBook-Pro Bug_manager % cat utils/tencent/cos.py 


from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
from django.conf import settings

def create_bucket(bucket, region='ap-beijing'):
    # region = 'ap-beijing'  # 替换为用户的 Region (桶的区域)
    token = None  # 使用临时密钥需要传入 Token,默认为空,可不填
    scheme = 'https'  # 指定使用 http/https 协议来访问 COS,默认为 https,可不填

    config = CosConfig(Region=region, SecretId=settings.TENCENT_COS_ID, SecretKey=settings.TENCENT_COS_KEY, Token=token, Scheme=scheme)
    # 2. 获取客户端对象
    client = CosS3Client(config)
    # 参照下文的描述。或者参照 Demo 程序,详见 https://github.com/tencentyun/cos-python-sdk-v5/blob/master/qcloud_cos/demo.py

    # 创建存储桶
    client.create_bucket(
        Bucket=bucket,
        ACL="public-read"  # private  /  public-read / public-read-write
    )


def upload_file(bucket, region, file_object, key):
    config = CosConfig(Region=region, SecretId=settings.TENCENT_COS_ID, SecretKey=settings.TENCENT_COS_KEY)
    client = CosS3Client(config)

    # upload_file_from_buffer 上传body
    response = client.upload_file_from_buffer(
        Bucket=bucket,
        Body=file_object,  # 文件对象
        Key=key  # 上传到桶之后的文件名
    )

    # https://manager-1251317460.cos.ap-chengdu.myqcloud.com/p1.png
    # 返回上传后图片访问地址
    return "https://{}.cos.{}.myqcloud.com/{}".format(bucket, region, key)

  • 第七步 :views 上传图片视图
...
...
from utils.encrypt import uid
from utils.tencent.cos import upload_file

...
...
@csrf_exempt
def wiki_upload(request, project_id):
    """ markdown插件上传图片 """
    # markdown 接收返回的固定格式
    result = {
        'success': 0,
        'message': None,
        'url': None
    }

    # 文件对象
    image_object = request.FILES.get('editormd-image-file')

    if not image_object:
        result['message'] = "文件不存在"
        return JsonResponse(result)

    ext = image_object.name.rsplit('.')[-1]
    key = "{}.{}".format(uid(request.bug_manager.user.mobile_phone), ext)
    image_url = upload_file(
        request.bug_manager.project.bucket, # 桶
        request.bug_manager.project.region, # 桶区域
        image_object,
        key
    )
    result['success'] = 1
    result['url'] = image_url   # 返回上传后图片路径
    return JsonResponse(result)
  • 上传图片

image

  • 第八步 :解决报错,在setting中进行配置
X_FRAME_OPTIONS = 'SAMEORIGIN'  # 表示该页面可以在相同域名页面的franme展示

image

posted @ 2021-08-07 17:57  SRE运维充电站  阅读(589)  评论(0编辑  收藏  举报