文件存储方案

  • 在项目中,如用户头像、文章图片等数据往往需要使用单独的文件存储系统来保存
  • 企业中常见的存储方案有两种:
    a.搭建分布式存储系统, 如FastDFS
    数据量非常大, 具备相应的运维管理人员
    b.第三方存储
    运维成本低, 安全可靠

七牛云

七牛云作为老牌云存储服务商, 提供了优质的第三方存储服务
官方网站 七牛云

使用步骤

  • 注册用户, 需要实名认证
  • 新建存储空间
  • 默认生成测试域名, 有效期为一个月(要长期使用则需要绑定自己的域名)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
以图形化的方式上传文件
在这里插入图片描述
直接在使用的地方引用外连接就可以访问了
在这里插入图片描述

交互过程
七牛云提供了多种语言的工具包以供开发者使用, 所以上传文件可以有以下两种形式:

  1. 由前端直接上传文件到七牛
  2. 由前端先将文件上传到后端web应用, 再由web应用上传七牛云

实际开发中应该 优先考虑客户端直接发送数据给七牛云, 交互过程如下:

在这里插入图片描述
此处为了学习七牛云工具的使用, 为以后python作为客户端进行第三方存储进行知识储备, 先将头像数据上传到web应用再发给七牛云进行存储, 交互过程如下:

在这里插入图片描述

在这里插入图片描述

安装
直接安装:

pip install qiniu
或
easy_install qiniu

上传代码:

# -*- coding: utf-8 -*-
# flake8: noqa
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, version='v2') 
print(info)
assert ret['key'] == key
assert ret['hash'] == etag(localfile)

封装成函数,进行相应的配置
在这里插入图片描述

在这里插入图片描述

from qiniu import Auth, put_file, etag

def upload_file(data):

    '''
    上传文件到七牛云
    :param data 要上传的二进制数据
    :return: 头像url
    '''
    #需要填写你的 Access Key 和 Secret Key
    access_key = 'gSMqHNP7ugq3S53n48vdcKQkGdsylzT5o4Bkrr5a'
    secret_key = '****************************************'
    #构建鉴权对象
    q = Auth(access_key, secret_key)
    #要上传的空间
    bucket_name = 'jibu123'
    #上传后保存的文件名(如果设置为None,七牛云会自动生成文件名,可以保证文件名的唯一性,本质就是使用文件的哈希值作为文件名)
    # 哈希函数:将任意内容转为定长的字符串,而且具有唯一性,一般称哈希值也叫做文件的指纹
    #比如第一次上传a文件第二次再上传a图片就会特别快,就是因为两次哈希值一样,所以就不用再上传了
    key = None
    #生成上传 Token,可以指定过期时间等
    token = q.upload_token(bucket_name, key, 3600)
    ret, info = put_file(token, key, data)
    if info.statis_code==200: # 上传成功
    #       域名是固定的                               可以获取到上传后的文件名
        return 'http://r8fj0mnul.hn-bkt.clouddn.com/' + ret.get('key')
    else:
        raise BaseException(info.error)
if __name__ == '__main__':
    with open('kobe.png','rb') as f:
        file_bytes = f.read()
        file_url = upload_file(file_bytes)

项目集成

  • 在 app/settings/config.py文件中设置 七牛云 的相关配置
class DefaultConfig:
    """默认配置"""
    ...

    # 七牛云
    QINIU_ACCESS_KEY = 'kJ8wVO7lmFGsdvtI5M7eQDEJ1eT3Vrygb4SmR00E'
    QINIU_SECRET_KEY = 'rGwHyAvnlLK7rU4htRpNYzpuz0OHJKzX2O1LWTNl'
    QINIU_BUCKET_NAME = 'sh32'
    QINIU_DOMAIN = 'http://q7f6ieukf.bkt.clouddn.com/'
  • 在 /common/utils包中, 创建 img_storage.py文件, 在其中定义文件上传函数
from qiniu import Auth, put_data, etag
from flask import current_app


def upload_file(data):
    """
    上传文件到七牛云
    :param data: 要上传的二进制数据
    :return: 文件的名称
    """

    # 需要填写你的 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 = None  # 如果设置key=None, 则七牛云会自动生成名称(该名称会通过文件的哈希值生成, 不同文件的哈希值不同, 文件名不会出现冲突)
    # 哈希函数: 可以将任意长度的内容转为定长内容, 而且相同内容的哈希值一样, 不同内容的哈希值不同

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

    ret, info = put_data(token, key, data)
    if info.status_code == 200:  # 上传成功
        return current_app.config['QINIU_DOMAIN'] + ret.get('key')  # 获取并返回文件URL
    else:
        raise Exception(info.error)


if __name__ == '__main__':
    with open('123.jpg', 'rb') as f:
        data = f.read()  # 模拟前端上传的二进制数据
        file_url = upload_file(data)
        print(file_url)

PUT(全部更新)
PATCH(部分更新)

接口设计

# 上传头像
/user/photo
# 请求方式  
PATCH   form-data
# 请求参数  
photo   上传的头像文件

# 响应数据  json
{
  "photo_url": "www.xx.com/123.jpg"
}

上传文件视图

# /app/resources/user/profile.py 

...

from flask_restful.reqparse import RequestParser
from utils.parser import image_file
from utils.img_storage import upload_file
from app import db
from flask import current_app

...


class UserPhotoResource(Resource):
    method_decorators = [login_required]

    def patch(self):
        """修改头像"""
        # 获取参数
        userid = g.userid
        parser = RequestParser()
        # 如果没有指定type会账指转换为字符串类型
        parser.add_argument('photo', required=True, type=FileStorage, location='files')
        args = parser.parse_args()
        img_file = args.photo

        # f = request.files.get('photo')
        # f.save()
        # f.read()

        # 读取二进制数据
        img_bytes = img_file.read()

        # 上传到七牛云
        try:
            file_url = upload_file(img_bytes)
        except BaseException as e:
            return {'message': 'Thired Error: %s' % e, 'data': None}, 500

        # 将数据库中头像URL进行更新
        User.query.filter(User.id == userid).update({'profile_photo': file_url})
        db.session.commit()

        # 返回URL
        return {'photo_url': file_url}

3. 检查图片文件
python的内置模块imghdr中封装了检查文件是否为图片类型的函数what(), 可以利用该函数检查前端上传的数据是否为图片类型

import imghdr

# with open('222.png', 'rb') as f:
#     # 判断文件类型 一般是通过文件的头部字节进行匹配
#     content = f.read()
#     print(content)


if __name__ == '__main__':
    with open('666.jpg', 'rb') as f:
        # 判断文件是否为图片类型

        # 方式1: 传递 文件路径/文件对象
        # type = imghdr.what('666.jpg')
        # type = imghdr.what(f)
        # print(type)

        # 方式2: 传递 二进制数据
        data_bytes = f.read()
        type = imghdr.what(None, data_bytes)
        print(type)

检查图片封装


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

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐