flask-web ——对象存储——图片上传存储传输等

Posted 胖虎是只mao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flask-web ——对象存储——图片上传存储传输等相关的知识,希望对你有一定的参考价值。

1. 七牛云存储

需求
在项目中,如用户头像、文章图片等数据需要使用文件存储系统来保存

方案

  • 自己搭建文件系统服务
  • 选用第三方对象存储服务
    我们在项目中使用七牛云对象存储服务 http://www.qiniu.com

2. 使用

  • 注册
  • 新建存储空间
  • 使用七牛SDK完成代码实现
    七牛Python SDK 网址 https://developer.qiniu.com/kodo/sdk/1242/python

在这里插入图片描述

安装SDK

pip install qiniu

编码
七牛提供的上传代码参考示例

from qiniu import Auth, put_file, etag
import qiniu.config

#需要填写你的 Access Key 和 Secret Key
access_key = 'Access_Key'
secret_key = 'Secret_Key'

#构建鉴权对象
q = Auth(access_key, secret_key)

#要上传的空间
bucket_name = 'Bucket_Name'

#上传后保存的文件名
key = 'my-python-logo.png'

#生成上传 Token,可以指定过期时间等
token = q.upload_token(bucket_name, key, 3600)

#要上传文件的本地路径
localfile = './sync/bbb.jpg'

ret, info = put_file(token, key, localfile)
print(info)
assert ret['key'] == key
assert ret['hash'] == etag(localfile)

项目实现

from qiniu import Auth, put_file, etag, put_data
import qiniu.config
from flask import current_app


def upload_image(file_data):
    """
    上传图片到七牛
    :param file_data: bytes 文件
    :return: file_name
    """
    # 需要填写你的 Access Key 和 Secret Key
    access_key = current_app.config['QINIU_ACCESS_KEY']
    secret_key = current_app.config['QINIU_SECRET_KEY']

    # 构建鉴权对象
    q = Auth(access_key, secret_key)

    # 要上传的空间
    bucket_name = current_app.config['QINIU_BUCKET_NAME']

    # 上传到七牛后保存的文件名
    # key = 'my-python-七牛.png'
    key = None

    # 生成上传 Token,可以指定过期时间等
    token = q.upload_token(bucket_name, expires=1800)

    # # 要上传文件的本地路径
    # localfile = '/Users/jemy/Documents/qiniu.png'

    # ret, info = put_file(token, key, localfile)
    ret, info = put_data(token, key, file_data)

    return ret['key']

上传头像、身份证图片接口
common/utils/parser.py

import imghdr

def image_file(value):
    """
    检查是否是图片文件
    :param value:
    :return:
    """
    try:
        file_type = imghdr.what(value)
    except Exception:
        raise ValueError('Invalid image.')
    else:
        if not file_type:
            raise ValueError('Invalid image.')
        else:
            return value

toutiao/resources/user/profle.py

class PhotoResource(Resource):
    """
    用户图像 (头像,身份证)
    """
    method_decorators = [login_required]

    def patch(self):
        file_parser = RequestParser()
        file_parser.add_argument('photo', type=parser.image_file, required=False, location='files')
        file_parser.add_argument('id_card_front', type=parser.image_file, required=False, location='files')
        file_parser.add_argument('id_card_back', type=parser.image_file, required=False, location='files')
        file_parser.add_argument('id_card_handheld', type=parser.image_file, required=False, location='files')
        files = file_parser.parse_args()

        user_id = g.user_id
        new_user_values = {}
        new_profile_values = {}
        return_values = {'id': user_id}

        if files.photo:
            try:
                photo_url = upload_image(files.photo.read())
            except Exception as e:
                current_app.logger.error('upload failed {}'.format(e))
                return {'message': 'Uploading profile photo image failed.'}, 507
            new_user_values['profile_photo'] = photo_url
            return_values['photo'] = current_app.config['QINIU_DOMAIN'] + photo_url
            need_delete_profile = True

        if files.id_card_front:
            try:
                id_card_front_url = upload_image(files.id_card_front.read())
            except Exception as e:
                current_app.logger.error('upload failed {}'.format(e))
                return {'message': 'Uploading id_card_front image failed.'}, 507
            new_profile_values['id_card_front'] = id_card_front_url
            return_values['id_card_front'] = current_app.config['QINIU_DOMAIN'] + id_card_front_url

        if files.id_card_back:
            try:
                id_card_back_url = upload_image(files.id_card_back.read())
            except Exception as e:
                current_app.logger.error('upload failed {}'.format(e))
                return {'message': 'Uploading id_card_back image failed.'}, 507
            new_profile_values['id_card_back'] = id_card_back_url
            return_values['id_card_back'] = current_app.config['QINIU_DOMAIN'] + id_card_back_url

        if files.id_card_handheld:
            try:
                id_card_handheld_url = upload_image(files.id_card_handheld.read())
            except Exception as e:
                current_app.logger.error('upload failed {}'.format(e))
                return {'message': 'Uploading id_card_handheld image failed.'}, 507
            new_profile_values['id_card_handheld'] = id_card_handheld_url
            return_values['id_card_handheld'] = current_app.config['QINIU_DOMAIN'] + id_card_handheld_url

        if new_user_values:
            User.query.filter_by(id=user_id).update(new_user_values)
        if new_profile_values:
            UserProfile.query.filter_by(id=user_id).update(new_profile_values)

        db.session.commit()

        return return_values, 201

3. 遇到的问题

token expired token过期

设置服务时间

查看时间 date
yum install -y ntpdate
ntpdate cn.pool.ntp.org			//自动同步网络时间

4.判断数据是否是图片

pip install imghdr

# imghdr -> image header

一个具体的二进制数据文件到底是哪种类型的文件(是图片还是普通的文本文件),在文件的二进制数据中的前几位标名文件类型的

def image_file(value):
    """
    检查是否是图片文件
    :param value:
    :return:
    """
    try:
        file_type = imghdr.what(value)
    except Exception:
        raise ValueError('Invalid image.')
    else:
        if not file_type:
            raise ValueError('Invalid image.')
        else:
            return value
5.上传图片

req.photo 取出了请求中的文件对象,通过read方法读取文件的二进制数据

        # req.photo 取出了请求中的文件对象,通过read方法读取文件的二进制数据
        file_name = upload(req.photo.read())
class PhotoResource(Resource):
    """
    用户图片资料的视图处理
    """
    method_decorators = [login_required]

    def patch(self):
        """
        修改用户的资料(修改用户头像)
        :return:
        """
        # 获取请求参数
        # 校验请求参数
        rp = RequestParser()
        rp.add_argument('photo', type=parser.image_file, required=True, location='files')
        req = rp.parse_args()

        # 业务处理
        # 上传到七牛
        # req.photo 取出了请求中的文件对象,通过read方法读取文件的二进制数据
        file_name = upload(req.photo.read())

        # 保存图片名(图片路径)到数据库
        # 1. 保存完整的图片路径,包含了域名,浪费空间
        # 2. 图片访问的域名实际上是可以设置的,如果修改了,数据库中如果保存了域名,数据库数据也得修改,不方便
        User.query.filter(User.id == g.user_id).update({'profile_photo': file_name})
        db.session.commit()

        # 构建返回数据
        photo_url = current_app.config['QINIU_DOMAIN'] + file_name
        return {'photo_url': photo_url}

如果在db.session.commit() 之后 数据库报错,flask会自己退出视图,然后自动进行了rollback,不需要手工去回滚。除非要对rollback进行特殊处理

6.CDN

使用第三方OSS服务的好处是集成了CDN服务,下面来了解一下什么是CDN。

CDN
全称:Content Delivery Network或Content Distribute Network即内容分发网络

是将源站内容分发至最接近用户的节点,使用户可就近取得所需内容,提高用户访问的响应速度和成功率。解决因分布、带宽、服务器性能带来的访问延迟问题,适用于站点加速、点播、直播等场景

基本思路
尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。

目的
解决因分布、带宽、服务器性能带来的访问延迟问题,适用于站点加速、点播、直播等场景。使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度和成功率。

控制时延无疑是现代信息科技的重要指标,CDN的意图就是尽可能的减少资源在转发、传输、链路抖动等情况下顺利保障信息的连贯性

CDN就是扮演者护航者和加速者的角色,更快准狠的触发信息和触达每一个用户,带来更为极致的使用体验

基本原理
在这里插入图片描述

最简单的CDN网络由一个DNS服务器和几台缓存服务器组成:

  • 当用户点击网站页面上的内容URL,经过本地DNS系统解析,DNS系统会最终将域名的解析权交给CNAME指向的CDN专用DNS服务器。
  • CDN的DNS服务器将CDN的全局负载均衡设备IP地址返回用户。
  • 用户向CDN的全局负载均衡设备发起内容URL访问请求。
  • CDN全局负载均衡设备根据用户IP地址,以及用户请求的内容URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求。
  • 区域负载均衡设备会为用户选择一台合适的缓存服务器提供服务,选择的依据包括:根据用户IP地址,判断哪一台服务器距用户最近;根据用户所请求的URL中携带的内容名称,判断哪一台服务器上有用户所需内容;查询各个服务器当前的负载情况,判断哪一台服务器尚有服务能力。基于以上这些条件的综合分析之后,区域负载均衡设备会向全局负载均衡设备返回一台缓存服务器的IP地址。
  • 全局负载均衡设备把服务器的IP地址返回给用户。
  • 用户向缓存服务器发起请求,缓存服务器响应用户请求,将用户所需内容传送到用户终端。如果这台缓存服务器上并没有用户想要的内容,而区域均衡设备依然将它分配给了用户,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉到本地。

哪些情况推荐使用CDN?

一般来说以资讯、内容等为主的网站,具有一定访问体量的网站 资讯网站、政府机构网站、行业平台网站、商城等以动态内容为主的网站 论坛、博客、交友、SNS、网络游戏、搜索/查询、金融等。提供http下载的网站 软件开发商、内容服务提供商、网络游戏运行商、源码下载等有大量流媒体点播应用的网站 拥有视频点播平台的电信运营商、内容服务提供商、体育频道、宽频频道、在线教育、视频博客等

以上是关于flask-web ——对象存储——图片上传存储传输等的主要内容,如果未能解决你的问题,请参考以下文章

在七牛云建对象存储用于上传图片

php表单上传图片到七牛云存储并返回地址……求具体流程~有代码更好

ueditor上传图片到七牛云存储(form api,java)

Android腾讯云对象存储上传图片

Android腾讯云对象存储上传图片

Android腾讯云对象存储上传图片