搞事情之使用七牛云的注意事项

2023-10-31

原文地址:PJ 的 iOS 开发日常

前言

本博客最初所采用的图床就是七牛,当时因为第一次使用图床之类的服务,没有进行一个比较好的筛选,并且没有考虑过多的细节,所以直接采用了七牛。经过一段时间后,因为博客访问量上去了,超出七牛每月的免费流量额度,平均每个月花费在 10 元左右。

因为写博客是一件非常费脑费精力的事情,再纠结钱就十分的没意思了,遂开始了寻找一个免费既好用的图床。一番搜寻后,发现了这个图床,用了快一年。在这一年的时间中从未出现过任何的异常情况,而且也提供 http 服务,原本打算把毕设相关的图片资源也使用这个图床,但考虑到该图床所提供的 http 服务能了较为有限,遂放弃。

最后继续调研,看了又拍云、七牛云、阿里云 OSS 、腾讯云 OSS,最后对比完还是选择了七牛云。这里要强烈向大家图床阿里云 OSS 服务,调用方式及其繁琐!仅仅至少上传一个图片而已,所写代码之啰嗦,实在难以让我提起兴趣。更遗憾的是,所有的 SDK 均未提供 Swift 版本,这都 9102 年了!!!

准备工作

七牛云的图床免费流量额度每个月只有 10 GB,超出这个范围后就会进行计费,团队初步估计 pv/uv 再怎么次,每天至少都会稳定在百人左右,所以要做一定的防刷手段。

存储区域

这部分的内容主要针对进行上传资源时提升服务的可靠性,如果产品的上传策略是图片先过一遍自己的服务器再上传至七牛,需要根据服务器架设地点进行选择;如果产品策略是客户端上传,则考虑目标群体大部分所在区域。不过就目前情况来看,PIGPEN 因为只涉及到图片资源的上传和下载,且大部分用户在华北地区,故选择了华北地区。当然,最后的收费也会提升一些。

空间选择

七牛云提供了公开空间私有空间

  • 公开空间:可通过文件对象的 URL 直接访问;
  • 私有空间:文件对象的访问则必须获得拥有者的授权才能访问。

公开空间,符合博客、社区、论坛等属性产品的定位,但不适合做内容闭环的产品,比如微信、QQ 以及 PIGPEN 等,并不希望产品中所产生的内容被产品外的用户所知,反而私有空间是正确选择,可对需要访问的资源设置 IP 访问次数和黑白名单限制、超时限制、浏览器是否能直接访问等等防刷手段。PIGPNE 选择了私有空间。

域名管理

加速域名

七牛云的测试域名 30 天后会进行自动回收,所以各位同学确定一定要使用七牛云作为图床后,请尽快配对加速域名。该加速域名要求必须通过中国大陆 ICP 备案。推荐重新配置一个新的二级域名作为七牛云的加速域名。

需要注意的是,配置完加速域名后,七牛会让我们增加一条 CNAME 解析,当访问这个域名时,实际上转发给记录值中所填写的真实域名。配置完毕后,如下图所示:

假设我们在七牛上配置完毕加速域名,生成的加速域名为 aha.pjhubs.com.qiniudns.com,在“主机记录”部分,需要填入的应该是我们所新增的二级域名 aha。配置妥当后,一般等待十分钟左右再去七牛控制台中查看相关信息即可。

访问控制

对于资源计费的图床,最担心的就是资源被刷了,好在七牛在四种访问控制的方法:Referer 防盗链时间戳防盗链回源鉴权IP 黑白名单

Referer 防盗链

Referer 防盗链:这里的 Referer 指的是 HTTP 头部的一个字段,也称为 HTTP 来源地址(HTTP Referer),用来表示从哪儿链接到目前的网页,采用的格式是 URL。换句话说,借着 HTTP Referer 头部网页可以检查访客从哪里而来,这也常被用来对付伪造的跨网站请求。(来源七牛官方文档)

如果我们直接通过浏览器打开一个链接,此时该请求的 Referer 字段为空,这是一个“凭空产生”的 HTTP 请求,并不是从一个地方链接过去的。

经过一番的思考,设置白名单且允许空 Referer 进行访问。原意是想着不允许空 Referer 访问的,但是在客户端上对图片资源发起的请求就是空 Referer 啊~遂放弃。

IP 黑白名单

这是解决 IP 盗刷的最终解决手段,但目前还未遇到真实盗刷场景,后续再补~

图片处理

七牛居然非常贴心的提供了一定的图片处理功能,对于一个内容产出方,最需要的就是图片资源的各种缩略图、统一水印等等功能,而且全部都是免费功能!!!

实际操作

整套七牛的图片上传流程可用下图进行概括:

后端

PIGPEN 整体后端都是基于 python 技术栈,所以选择了七牛云的 python SDK。通过 pip3 install qiniu 后,根据已有业务编写了以下两个方法:

from qiniu import Auth

def create_upload_image_token(count, key):
    """
    七牛不支持多图上传,根据官方文档描述,只能在业务层循环针对每个图生成对应 token
    :param count: 需要生成的 token 个数
    :param key: 文件名前缀
    :return 生成的 token 列表
    """

    jsons = []
    while count > 0:
        # 构建鉴权对象
        q = Auth(settings.QINIU_ACCESS_KEY, settings.QINIU_SECRET_KEY)
        # 要上传的空间
        bucket_name = '你的私有空间名'
        # 文件名
        k = bucket_name + key + str(int(time.time())) + str(count) + '.jpeg'
        token = q.upload_token(bucket_name, k)

        json = {
            'img_token': token,
            'img_key': k
        }

        jsons.append(json)
        count -= 1

    return jsons


def create_full_image_url(keys):
    """
    拼接获取完成后的图片 url
    :param keys: 从客户端发送来的 keys,遍历出的每一个 key 代表一个文件名
    :return image_urls: 返回拼接完成后的图片 url 数组
    """
    q = Auth(settings.QINIU_ACCESS_KEY, settings.QINIU_SECRET_KEY)
    bucket_name = 'pigpenimg.pjhubs.com'

    image_urls = []
    for index, key in enumerate(keys):
        base_url = 'http://%s/%s' % (bucket_name, key)
        private_url = q.private_download_url(base_url, expires=3600)

        image_urls.append(private_url)

    return image_urls
复制代码

在构造 token 的方法 create_upload_image_token 中,一开始我并没有做文件名的指定,导致最终生成的图片资源链接并没有后缀名,这就导致了在客户端进行资源请求时,加载失败,但通过浏览器进行访问却是正常的。出现这种问题你可以选择跟我一样的方法:在该方法中指定文件名,并加上资源类型后缀

在业务层中,在接受客户端上传的 keys API 时,考虑到了多图上传的情况,在针对多 keys 情况考虑了到底是直接转为 json 请求体直接上传,还是把多个 keys 进行拼接的问题,按道理,后端接口部分应该直接解析客户端发送的 json 请求体,直接从中解析出所需要的数据,但不知道 python 具体应该怎么操作,这部分内容拖得也比较久了,就先不优化了,所以最终采用了第二种方案。

客户端

正如前文所说,原意是打算找到一家提供 Swift 版本 SDK 的服务商就直接用了,但是经过一番的调研后,发现根本没有,不管大小公司都没有提供 Swift 版本!!!真是太气人了!

最后没办法,只能选择业内老油条七牛,好在七牛提供 Objective-C 版本 SDK 调用方式足够简洁。PIGPEN 客户端整体基于 CocoaPods 进行依赖管理,只需要在依赖配置文件 podfile 中增加 use_frameworks! 即可把七牛 SDK 通过动态链接库的方式直接引入,而不用进行桥接:

platform :ios, '10.0'
target 'PIGPEN' do
    use_frameworks!

    pod 'Qiniu'
    ...
end
复制代码

为了方便使用七牛的上传服务,抽离出了获取生成的 token 和通过 key 换取授权图片 url 的两个方法,需要注意的是七牛的 iOS SDK 并未直接提供 UIImage 的上传方法,而是通过 Data 或者 PHAsset 类型参数进行上传。

原本是想着直接把 UIImage 转为 Data 后进行上传,但不知是因为七牛 Objective-C SDK 转换成的 Swift 方法有问题还是其它的一些原因,部分参数一直未识别,最后只能更换为了使用 PHAsset 资源进行上传。调通后发现,在 PIGPEN 中实际上就是通过读取相册中的部分照片进行上传,而从相册中获取照片全都是 PHAsset 类型的数据,如果要转为 UIImage 中间还会经过一个异步操作,确实是多了一些不必要的操作。关于 iOS 中的自定义相册,如果你感兴趣可以参考这篇文章

//
//  PJImageUpload.swift
//  PIGPEN
//
//  Created by PJHubs on 2019/1/2.
//  Copyright © 2019 PJHubs. All rights reserved.
//

import Foundation
import Photos
import Qiniu

class PJImageUploader {
    /// 上传图片
    class func upload(assets: [PHAsset],
                      complateHandler: @escaping (([String], [String]) -> Void),
                      failuredHandler: @escaping ((PJNetwork.Error) -> Void)) {
        PJNetwork.shared.requstWithGet(path: URL.upload.rawValue,
                                       parameters: ["imageCount": String(assets.count)],
                                       complement: { (dataDict) in
                                        if dataDict["msgCode"]?.intValue == 0 {
                                            var dataDict = dataDict["msg"]!
                                            let tokens = dataDict["upload_tokens"].arrayValue
                                            // `setKey` 方法参数
                                            var keys = ""
                                            // complateHandler 闭包回调参数
                                            var complateKeys = [String]()
                                            for c_i in 0..<assets.count {
                                                let token = tokens[c_i]["img_token"].string
                                                let key = tokens[c_i]["img_key"].string
                                                complateKeys.append(key!)
                                                // 七牛上传
                                                QNUploadManager()?.put(assets[c_i],
                                                                       key: key,
                                                                       token: token,
                                                                       complete: { (info, key, respDict) in
                                                                        guard let respDict = respDict else { return }
                                                                        // key 即为文件名。拼接完成后一次性丢给 API
                                                                        let key = respDict["key"]
                                                                        keys += "," + String(key as! String)
                                                    
                                                                        if c_i == assets.count - 1 {
                                                                            keys.removeFirst()
                                                                            setKey(key: keys, complateHandler: { (imgUrls) in
                                                                                complateHandler(imgUrls, complateKeys)
                                                                            }, failuredHandler: { (error) in
                                                                                falierHandler(error)
                                                                            })
                                                                        }
                                                }, option: nil)
                                            }
                                        } else {
                                            let error = PJNetwork.Error(errorCode: dataDict["msgCode"]?.intValue,
                                                                        errorMsg: dataDict["msg"]?.string)
                                            failuredHandler(error)
                                        }
        }) { (errorString) in
            falierHandler(PJNetwork.Error(errorCode: nil, errorMsg: errorString))
        }
    }
    
    /// 上传 keys 且换回图片完整 url
    class func setKey(key: String,
                complateHandler: @escaping ((([String]) -> Void)),
                failuredHandler: @escaping ((PJNetwork.Error) -> Void)) {
        PJNetwork.shared.requstWithPost(path: URL.setKey.rawValue,
                                        parameters: ["keys": key],
                                        complement: { (dataDict) in
                                            if dataDict["msgCode"]?.intValue == 0 {
                                                var dataDict = dataDict["msg"]!
                                                let keys = dataDict["image_urls"].array
                                                if keys != nil {
                                                    var k = [String]()
                                                    for key in keys! {
                                                        k.append(key.string!)
                                                    }
                                                    complateHandler(k)
                                                }
                                            }
        }) { (errorString) in
            let error = PJNetwork.Error(errorCode: nil, errorMsg: errorString)
            failuredHandler(error)
        }
    }
}

// MARK: - URL
extension PJImageUploader {
    enum URL: String {
        case upload = "realPet/uploadToken"
        case setKey = "realPet/setKeys"
    }
}
复制代码

总结

以上就是使用七牛云作为 PIGPEN 图床的对接历程,出现的问题主要在加速域名的配置和私有空间图片的访问上,中间也发了几次工单和七牛云工程师询问了几个问题,整体来看反应速度十分迅速,不管是工作日还是周末,工单回复最长回复耗时不到 10 分钟!

最后再吐槽一句,国内的服务商什么时候才能提供 Swift 版本的 SDK 啊!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

搞事情之使用七牛云的注意事项 的相关文章

随机推荐

  • Go mod 全部操作命令

    一 基础理论 shell 1 Go mod 初始化 go mod init 模块名 2 Go mod 下载到本地Cache go mod download 2 Go mod 清理本地Cache go clean modcache 3 Go
  • IDEA查看调用方法代码上一步和下一步

    IDEA默认上一步是CTRL ALT 箭头 IDEA默认下一步是CTRL ALT 箭头
  • 2020暨南大学计算机专硕考研经验分享

    具体的研究生招生录取情况 初试复试比例 和初复试的流程可以看下面链接这篇 总结得很好 2020暨南大学计算机考研经验分享 精品 含录取名单 学习等资料获取 前期都是各位学长的帮助 真的非常感谢 尤其了知乎上分享经验的18级李学长 集中资料
  • stata怎么判断是否存在异常值_异常值的识别与处理,看这一篇就够了

    原标题 异常值的识别与处理 看这一篇就够了 在数据分析工作中 面对收集而来的数据 数据清洗是首要环节 而异常值处理是其中的一个重要部分 下面就给大家介绍一下如何处理数据中的异常值 一 异常值判断 何为异常值 异常值 指的是样本中的一些数值明
  • 关于CLion有时找不到标准库的解决方案

    关于CLion有时找不到标准库的解决方案 CLion是linux下C 开发的利器 出色的语法高亮 支持cmake工程让同类IDE望尘莫及 但是我在实际开发中遇到了标准库 STL 相关的语法高亮不能正常运行的问题 问题情境 我们用UBUNTU
  • PyQt QTextEdit 详细用法示例 Python

    PyQt QTextEdit 详细用法示例 Python QTextEdit 是 PyQt 中用于显示和编辑文本的小部件之一 它提供了丰富的功能 包括文本格式化 文本样式 撤销和重做操作等 在本文中 我们将探讨 QTextEdit 的详细用
  • unity could not produce class with id 210 --- TerrainInstance 其实是 UnityEngine.CoreModule.Sorting

    Strip Engine Code 幾重 張 巡 罠 https qiita com warapuri items e2562e9535bfae5013d0 More than 1 year has passed since last up
  • 在windows下详解:大端对齐和小端对齐

    计算机的内存最小单位是什么 是BYTE 是字节 一个大于BYTE的数据类型在内存中存放的时候要有先后顺序 高内存地址放整数的高位 低内存地址放整数的低位 这种方式叫倒着放 术语叫小端对齐 电脑X86和手机ARM都是小端对齐的 高内存地址放整
  • IP地址、子网掩码、网络地址、广播地址、IP网段

    文章目录 IP地址 IP地址分类 子网掩码 网络地址 广播地址 IP网段 本文主要讨论iPv4地址 IP地址 实际的 IP 地址是一串32 比特的数字 按照 8 比特 1 字节 为一组分成 4 组 分别用十进制表示然后再用圆点隔开 这就是我
  • 关于CMAKE 报错CMAKE_CUDA_ARCHITECTURES的问题

    背景 新版本cmake 增加了CMAKE CUDA ARCHITECTURES检测 某些手动安装cuda的同学会遇到该报错问题 该问题不影响代码 只是cmake内部的编译设置 cmake 3 23版本该问题报错为 CMAKE CUDA AR
  • java渗透测试基础之——RMI

    一 概述 RMI全称是Remote Method Invocation 远程方法调用 是专为Java环境设计的远程方法调用机制 远程服务器提供API 客户端根据API提供相应参数即可调用远程方法 由此可见 使用RMI时会涉及到参数传递和结果
  • 利用python建立股票量化交易系统(一)——小市值选股票模型

    从今天开始正式开启我的博客之旅 博客内容全部是我自己的量化心得 主要还是为自己将来中工作之中遇到相似问题 可以方便的找到答案 如果能帮到有相似问题的其他同学 我也很开心 如果帮不到的话 不喜勿喷 如果文章中有什么不对的地方 欢迎批评指正 建
  • 新版QQ代挂系统源码四套模板

    介绍 代挂源码 代挂对接教程源码已简洁版优化框架数据 对接代挂教程均在 压缩文件里 源码进行了优化 原后门已清楚掉 上传源码解压即可安装访问网址 网盘下载地址 https zijiewangpan com VylBgT70aLu 图片
  • 连接Charles后,小程序无法打开,提示“运行失败”解决方法

    今天在使用Charles抓包的过程中 手机端安装了证书 并且证书安装成功 使用手机浏览器可以正常抓包 但是在使用微信打开小程序准备测试时 无法打开 并且提示 运行环境失败 于是做了以下几个操作 最后可以成功抓包 1 微信版本升级 将微信卸载
  • Java代码中如何判断一个HashMap对象是否为空呢?

    转自 Java代码中如何判断一个HashMap对象是否为空呢 下文讲述检测HashMap集合对象是否无元素的方法分享 如下所示 实现思路 使用isEmpty方法即可检测HashMap中的元素是否为空 isEmpty 语法 hashmap i
  • 学习Javascript的书籍

    原文地址 http www ruanyifeng com blog 2008 01 javascript book recommendation html 作者 阮一峰 日期 2008年1月 9日 昨天 ppip同学留言 你的js主要是用什
  • SQL Server(2019)数据库----数据查询(数据库系统概论第五版)

    目录 一 课本例题查询 1 查询全体学生的姓名及其出生年份 2 查询全体学生的姓名 出生年份和所在的院系 要求用小写字母表示系名 3 查询选修了课程的学生学号 4 查询不是数学系 计算机系学生的姓名和性别 5 查询选修了3号课程的学生的学号
  • Redis面试题(四)

    文章目录 前言 一 锁互斥机制 二 watch dog 自动延期机制 三 可重入加锁机制 四 释放锁机制 五 上述 Redis 分布式锁的缺点 六 使用过 Redis 分布式锁么 它是怎么实现的 总结 前言 锁互斥机制 watch dog
  • QT中信号与槽的连接

    本文章主要通过代码的形式讲解QT中connect函数对于信号和槽函数的连接 include mainwindow h include ui mainwindow h include
  • 搞事情之使用七牛云的注意事项

    原文地址 PJ 的 iOS 开发日常 前言 本博客最初所采用的图床就是七牛 当时因为第一次使用图床之类的服务 没有进行一个比较好的筛选 并且没有考虑过多的细节 所以直接采用了七牛 经过一段时间后 因为博客访问量上去了 超出七牛每月的免费流量