七牛云--开发笔记
开发文档:https://developer.qiniu.com/kodo/sdk/1241/php
一、下载官方SDK :
https://github.com/qiniu/php-sdk/releases?ref=developer.qiniu.com
下载源代码包后,解压到您的项目中。 然后在您的项目中引入 autoloader:
基本使用方法
安装好 SDK 后,接下来介绍如何使用 SDK。在使用 SDK 之前,需要注册一个七牛帐号,并登录控制台获取一对有效的AccessKey和SecretKey,并且详细阅读[安全机制][securityHref]以进一步了解如何正确使用和管理密钥。
引入SDK
无论你是通过哪种方式安装的 SDK,只需要一句简单的 require 语句将安装好的SDK包含到你的项目中。请参考如下表格,找到适合你的安装方式,将SDK引入到你的项目中。请用你项目中的实际路径替换 path_to_sdk。
安装方式 | 引入SDK |
---|---|
Composer 安装 | require 'path_to_sdk/vendor/autoload.php'; |
下载Composer安装包 | require 'path_to_sdk/vendor/autoload.php'; |
下载源码安装包 | require 'path_to_sdk/autoload.php'; |
SDK结构
可以直接查看我们的SDK目录,即可大致了解我们SDK的结构。
- 最外层包含:配置文件, 鉴权和一些公共的函数。
- HTTP 目录主要包含了一些对 http 进行封装的类,这块你可以不用关心。
- Storage 目录主要包含两大块:Bucket 中文件的管理和文件的上传。
- Processing 目录主要包含文件的处理,文件处理又包含两个方面:同步处理和异步处理。
为了帮助您更好的了解和使用我们的sdk,我们还提供了各种使用示例。 如果使用中遇到问题,可以通过Github或者我们的工单进行反馈。下面我们对一些关键点进行简单的介绍,方便您快速入门。
鉴权类
在使用七牛的SDK的过程中鉴权是很重要的一块,不管是上传鉴权,下载签权, 还是回调的签权。 PHP SDK 中的 Auth 类封装了所有的鉴权方式。 所以在使用 PHP SDK 时基本都会先对鉴权类进行初始化:
-
-
require 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
-
// 用于签名的公钥和私钥
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
-
// 初始化签权对象
-
$auth = new Auth($accessKey, $secretKey);
初始化好Auth 类后,就可以调用相应的方法了,比如生成私有下载链接方法 privateDownloadUrl,获取上传Token方法 uploadToken,验证回调方法 verifyCallback等。
上传类
在上传类 UploadManager 中主要负责文件的上传, 七牛的文件上传分为两种上传方式:表单上传和分片上传。当然在PHP SDK中你不用关心这个细节,只需要调用 UploadManager 中的上传方法即可。UploadManager 中的上传方法会根据上传文件的大小选择不同的上传方式,小于4MB的进行表单上传,大于4MB的进行分片上传。 以下一个简单的示例使你更清晰地了解如何初始上传对象:
-
-
require 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Storage\UploadManager;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
$auth = new Auth($accessKey, $secretKey);
-
-
// 空间名 https://developer.qiniu.io/kodo/manual/concepts
-
$bucket = 'Bucket_Name';
-
-
// 生成上传Token
-
$token = $auth->uploadToken($bucket);
-
-
// 构建 UploadManager 对象
-
$uploadMgr = new UploadManager();
空间名请参阅空间。
资源管理类
在资源管理类 BucketManager 中主要负责空间中文件的管理: 复制,移动,删除,获取元信息,修改Mime,拉取文件到七牛空间,列取文件。 以下一个简单的示例使你更清晰地了解如何初始空间管理对象:
-
-
require 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Storage\BucketManager;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
-
//初始化Auth状态
-
$auth = new Auth($accessKey, $secretKey);
-
-
//初始化BucketManager
-
$bucketMgr = new BucketManager($auth);
触发持久化类
触发持久化类 PersistentFop 中主要负责对空间中已上传的文件进行异步处理,并对异步处理的结果进行持久化操作。比如对已上传的视频文件进行转码,拼接,切片,水印等处理,并将处理后的结果保存在空间中。
-
-
-
require_once __DIR__ . '/../autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Processing\PersistentFop;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
$auth = new Auth($accessKey, $secretKey);
-
-
// 要转码的文件所在的空间
-
$bucket = 'Bucket_Name';
-
-
// 转码时使用的队列名称
-
$pipeline = 'abc';
-
-
// 初始化
-
$pfop = new PersistentFop($auth, $bucket, $pipeline);
场景化示例
接下来我们,对一些常见的使用场景,提供一些简单的Demo,使您更快速的接入七牛。
Hello, World!
在了解了基本概念和编程模型后,我们现在以最短的篇幅将第一个云存储服务的示例程序跑起来。该示例的目标是:将一个本地小文件上传到指定的存储空间中。
我们先写一个简单的 HTML 表单来供用户上传到七牛存储空间信息并选择一个本地文件。在点击提交按钮后,浏览器会发送一个 HTTP 请求到七牛上传域名执行文件上传动作。
-
<form method="post" action="http://up.qiniu.com" enctype="multipart/form-data">
-
<input name="token" type="hidden" value="<upload_token>">
-
<input name="file" type="file" />
-
<input type="submit" value="上传"/>
-
</form>
也许你注意到了这个上传表单有一个token
字段,所以想要使用 form 直传七牛一个文件,必须在页面加载的时候,向服务端请求token
并设置到这个表单项中。服务端生成这个token
涉及到我们 SDK 中提供的方法:Qiniu\Auth
。
关键代码如下所示:
-
use Qiniu\Auth;
-
-
$bucket = '<your_bucket>';
-
$accessKey = '<your_access_key>';
-
$secretKey = '<your_secret_key>';
-
$auth = new Auth($accessKey, $secretKey);
-
-
$upToken = $auth->uploadToken($bucket);
-
-
echo $upToken;
可以看出,以上代码主要做了以下几步工作:
- 使用你的 AccessKey 及 SecretKey 用于初始化
Auth
对象 - 调用初始化的对象
Auth
对象的方法uploadToken
来生成上传token
到这里为止,第一个示例就可以运行起来了。您可以查看该示例的在线演示。您也可以直接获取和查看该示例的源代码。
小结
这个示例仅用于演示如何使用 PHP SDK 快速运行第一个云存储服务的程序。本事例尽可能简单的展现七牛的表单上传,因此请大家仅将本示例作为学习 PHP SDK 的一个步骤,而不是在产品环境中应用。
一个完整的移动应用
我们将详细介绍如何使用 PHP SDK 与其他端配合构建一个类似于微信朋友圈的移动应用。
在这个移动应用里终端用户可以进行如下操作:
- 使用预设的帐号密码登录(不支持注册新帐号)
- 查看自己和朋友分享的照片以及描述(不支持评论)
- 上传一张新图片,并添加一段描述。
总体架构
对于此类应用,我们推荐的总体架构如下图所示:
对于此架构有以下关键要点:
- AK/SK 只能在业务服务器端使用,如果将 AK 和 SK 同时保存或者传输到移动端会有严重的信息泄露风险。
- AK/SK 是企业帐号信息,业务服务器需要有自己的帐号数据库,对应到每一个终端用户的帐号信息。
- 由于不同的业务都需要存放一些跟文件相关的描述信息,且图片又属于不同的终端用户,因此需要维护文件管理表,以管理文件的描述信息和所有者关系。
基于以上这些关键要点,我们来设计和实现我们的 PHP 版本的业务服务器。
接口设计
我们来简单的设计我们的业务服务器接口。
帐号验证:
-
POST /login.php HTTP/1.1
-
HOST: demos.qiniu.io
-
Accept: */*
-
Content-Type: application/x-www-form-urlencoded
-
-
-
uname=<username>&pwd=<password>
-
获取文件列表:
-
POST /files.php HTTP/1.1
-
HOST: demos.qiniu.io
-
Accept: */*
-
Cookie: <cookie>
-
Content-Type: application/x-www-form-urlencoded
获取上传授权,因为移动端并不知道 AK/SK 信息,客户端在需要上传文件时都需要向业务服务器发起一个获取上传授权的请求:
-
POST /uptoken.php HTTP/1.1
-
HOST: demos.qiniu.io
-
Accept: */*
-
Cookie: <cookie>
-
Content-Type: application/x-www-form-urlencoded
回调,移动端会直接上传文件到云存储服务,因此业务服务器不需要提供上传接口,但是需要提供一个供云存储服务在接收到文件后的回调接口。回调接口的响应内容会由云存储服务返回给移动端:
-
POST /callback.php HTTP/1.1
-
HOST: demos.qiniu.io
-
Accept: */*
-
Cookie: <cookie>
-
Content-Type: application/x-www-form-urlencoded
-
-
uid=<uid>&fname=<file_name>&fkey=<file_key>&desc=<description>
-
数据表,业务服务需要一个业务数据库的支撑以管理提到的帐号信息和文件信息。在本示例中我们选用 MySQL 来搭建业务数据库。帐号表:
字段名 | 字段类型 | 字段说明 | |
---|---|---|---|
id | int | 唯一标识。 | |
uid | int | 唯一用户标识。 | |
uname | char(128) | 用户名,格式为邮箱如 admin@example.com 。 | |
password | char(128) | 加密后的密码。 | |
status | int | 1 : active 0 : disabled |
文件信息表:
字段名 | 字段类型 | 字段说明 | |
---|---|---|---|
id | int | 唯一标识。 | |
uid | int | 用户唯一标识。 | |
fname | char(512) | 文件的显示名 | |
key | char(512) | 文件对应到云存储服务中的唯一标识。 | |
createTime | time | 上传时间。 | |
description | char(2048) | 文件的描述内容。 |
相应的 SQL 语句如下:
-
create database qspace;
-
-
CREATE TABLE users (
-
uid INT NOT NULL AUTO_INCREMENT,
-
uname VARCHAR(128) NOT NULL,
-
password VARCHAR(128) NOT NULL,
-
status INT,
-
PRIMARY KEY (uid)
-
);
-
-
CREATE TABLE files_info (
-
id INT NOT NULL AUTO_INCREMENT,
-
uid INT NOT NULL,
-
fname VARCHAR(512) NOT NULL,
-
fkey VARCHAR(512) NOT NULL,
-
createTime INT,
-
description VARCHAR(1024),
-
PRIMARY KEY (id),
-
FOREIGN KEY (uid) REFERENCES users(uid),
-
UNIQUE INDEX (id)
-
);
服务实现
接下来我们一步步的实现以上定义的接口。这些实现主要考虑流程的完整性,因此在安全性上没有做充分的考量,请不要直接使用于产品环境中。
配置文件
我们需要在一个地方存放全局的设置。本示例中我们直接用一个 config.php 文件包含若干全局变量的方式来解决这个问题。
-
-
class Config
-
{
-
const DB_NAME = 'qspace',
-
DB_USER = 'root',
-
DB_PWD = '****',
-
DB_HOST = '<your_db_host>',
-
ACCESS_KEY = '<your_access_key>',
-
SECRET_KEY = '<your_secret_key>',
-
BUCKET_NAME = '<your_bucket_name>';
-
}
帐号验证
帐号验证的逻辑非常简单,就是将请求中包含的用户信息与数据库表中的信息进行比对,因此不需要涉及 SDK 的功能。
-
-
require_once 'db.php';
-
-
session_start();
-
-
if(!isset($_POST['uname']) && !isset($_POST['pwd']))
-
{
-
http_response_code(401);
-
$resp = array('status' => 'failed', 'msg' => 'please input username & password!');
-
echo json_encode($resp);
-
return;
-
}
-
-
$uname = $_POST['uname'];
-
$_pwd = $_POST['pwd'];
-
-
$salt = 'Qiniu' . $uname;
-
$pwd = crypt($_pwd, $salt);
-
-
$stmt = $DB->prepare('select * from users where uname = :name');
-
$stmt->execute(array('name' => $uname));
-
-
$user = $stmt->fetch();
-
-
if ($user['password'] !== $pwd)
-
{
-
http_response_code(401);
-
$resp = array('status' => 'failed', 'msg' => 'incorrect username or password!');
-
echo json_encode($resp);
-
return;
-
}
-
-
$_SESSION['uid'] = $user['uid'];
-
$_SESSION['uname'] = $uname;
-
-
$resp = array('status' => 'ok', 'uname' => $uname);
-
echo json_encode($resp);
获取文件列表
同帐号验证功能一样,获取文件列表也只是查询数据库,因此不多做解释。
-
-
require_once 'db.php';
-
-
session_start();
-
-
$uid = $_SESSION['uid'];
-
if(!isset($uid))
-
{
-
header('location: login.php');
-
return;
-
}
-
-
$stmt = $DB->prepare('select * from files_info where uid = :uid');
-
$stmt->execute(array('uid' => $uid));
-
-
$files = $stmt->fetchAll();
-
-
echo json_encode($files);
获取上传授权
客户端在需要上传文件时都需要先向业务服务器发起一个获取上传授权的请求。 SDK 中的 Qiniu\Auth
类提供了 uploadToken($bucket, ...)
方法,可以非常便利的生成对应的上传授权。
-
-
require_once 'vendor/autoload.php';
-
require_once 'db.php';
-
require_once 'config.php';
-
-
use Qiniu\Auth;
-
-
session_start();
-
$uid = $_SESSION['uid'];
-
if(!isset($uid))
-
{
-
header('location: login.php');
-
return;
-
}
-
-
$bucket = Config::BUCKET_NAME;
-
$accessKey = Config::ACCESS_KEY;
-
$secretKey = Config::SECRET_KEY;
-
$auth = new Auth($accessKey, $secretKey);
-
-
$policy = array(
-
'callbackUrl' => 'http://172.30.251.210/callback.php',
-
'callbackBody' => '{"fname":"$(fname)", "fkey":"$(key)", "desc":"$(x:desc)", "uid":' . $uid . '}'
-
);
-
-
$upToken = $auth->uploadToken($bucket, null, 3600, $policy);
-
-
header('Access-Control-Allow-Origin:*');
-
echo $upToken;
回调
在收到回调时,通常表示一个文件已经成功上传。回调会包含该文件所对应的描述信息。因此业务服务器在收到回调后,需要将相应的文件信息写入到文件信息表中。
-
-
require_once 'db.php';
-
-
$_body = file_get_contents('php://input');
-
$body = json_decode($_body, true);
-
-
$uid = $body['uid'];
-
$fname = $body['fname'];
-
$fkey = $body['fkey'];
-
$desc = $body['desc'];
-
-
$date = new DateTime();
-
$ctime = $date->getTimestamp();
-
-
$stmt = $DB->prepare('INSERT INTO files_info (uid, fname, fkey, createTime, description) VALUES (:uid, :fname, :fkey, :ctime, :desc);');
-
$ok = $stmt->execute(array('uid' => $uid, 'fname' => $fname, 'fkey' => $fkey, 'ctime' => $ctime, 'desc' => $desc));
-
-
header('Content-Type: application/json');
-
if (!$ok)
-
{
-
$resp = $DB->errorInfo();
-
http_response_code(500);
-
echo json_encode($resp);
-
return;
-
}
-
-
$resp = array('ret' => 'success');
-
echo json_encode($resp);
服务监控
为了确认服务的正常运行,我们还实现了一个简单的监控页面以查看所有上传的图片。该页面假设admin
的管理员才有权访问。后端代码就是将用户上传的文件从数据库中列取出来。
-
-
require_once 'vendor/autoload.php';
-
require_once 'db.php';
-
-
if (!$_SESSION['logged'])
-
{
-
header('login.php');
-
}
-
-
$id = $_POST['id'];
-
if ($id)
-
{
-
$stmt = $DB->prepare('delete from files_info where id = :id');
-
$stmt->execute(array('id' => $id));
-
}
-
-
$stmt = $DB->prepare('select * from files_info');
-
$stmt->execute();
-
$files = $stmt->fetchAll();
-
-
$smarty = new Smarty();
-
$smarty->assign('files', $files);
-
-
$smarty->display('file_mgr.tpl');
前端页面使用 Bootstrap 的控件实现,并使用 Smarty 模板技术来循环生成最终页面:
-
{foreach from=$files item=file}
-
<tr>
-
<td><a href="{$file[uid]}">{$file["uid"]}</a></td>
-
<td>{$file["fname"]}</td>
-
<td>{$file["fkey"]}</td>
-
<td>{$file["description"]}</td>
-
<td>{$file["createTime"]}</td>
-
<td><a class="del" href="" data-fid="{$file['id']}">删除</a></td>
-
</tr>
-
{/foreach}
移动端实现
本示例包含一个 Android 客户端的实现。因为本文档的重心是结合例子讲解 PHP SDK 的使用,因此这里就不详细讲解如何实现 Android 客户端了。您可以下载和安装移动客户端的安装包,或查看移动客户端的源代码。
小结
您可以在 Android 手机或者模拟器上安装和运行本示例的移动端应用,上传一张图片,并查看图片列表和描述。这个示例的重点在于讲解一个推荐的产品架构,以及各个子系统如何协同工作。
接下来我们再围绕几个示例遍历七牛云提供的其他强大功能。接下来的示例都不会再像这个示例一样提供一个完整的移动互联网产品架构,而是把重点放在功能介绍上。您可以举一反三,快速的在本章介绍的示例上进行修改和加强,就可以快速开发出各种有趣的移动互联网应用。移动端安装包,或查看移动客户端的源代码,admin管理界面。
图片处理
我们将围绕一个功能相对完备的示例来讲解图片处理的主要用法,并顺便介绍 PHP SDK 的资源管理功能。
这个示例用户可以上传图片,选择图片处理模式,输入相应的处理参数,然后可以查看处理的结果。用户还可以点选不同的图片以显示图片的鉴黄信息和鉴别广告的信息, 以及获取图片的基本信息,平均色,exif信息等。
下面我们分步来实现这个示例。前端实现使用了Bootstrap和我们的js-sdk,后端使用 phpsdk 生成上传的 token 。
生成上传token
首先安装我们的 phpsdk,然后引入相应文件并调用接口生成上传token:
-
require_once 'vendor/autoload.php';
-
require_once 'config.php';
-
-
use Qiniu\Auth;
-
-
$bucket = Config::BUCKET_NAME;
-
$accessKey = Config::ACCESS_KEY;
-
$secretKey = Config::SECRET_KEY;
-
-
$auth = new Auth($accessKey, $secretKey);
-
$upToken = $auth->uploadToken($bucket);
-
-
$ret = array('uptoken' => $upToken);
-
-
echo json_encode($ret);
上传部分相关的 html 代码:
-
<div id="container">
-
<button id="pickfiles" class="btn btn-primary btn-lg btn-block" type="submit">上传图片</button>
-
</div>
上传对应的调用 jssdk 相关代码:
-
var uploader = Qiniu.uploader({
-
runtimes: 'html5,flash,html4', //上传模式,依次退化
-
browse_button: 'pickfiles', //上传选择的点选按钮,**必需**
-
uptoken_url: 'uptoken.php', //Ajax请求upToken的Url,**强烈建议设置**(服务端提供)
-
domain: 'http://rwxf.qiniudn.com/', //bucket 域名,下载资源时用到,**必需**
-
container: 'container', //上传区域DOM ID,默认是browser_button的父元素,
-
max_file_size: '100mb', //最大文件体积限制
-
flash_swf_url: 'plupload/Moxie.swf', //引入flash,相对路径
-
max_retries: 3, //上传失败最大重试次数
-
dragdrop: true, //开启可拖曳上传
-
drop_element: 'container', //拖曳上传区域元素的ID,拖曳文件或文件夹后可触发上传
-
chunk_size: '4mb', //分块上传时,每片的体积
-
auto_start: true, //选择文件后自动上传,若关闭需要自己绑定事件触发上传
-
init: {
-
'UploadProgress': function(up, file) {
-
$('#pickfiles').prop('disabled', true).html('图片上传中...');
-
},
-
'FileUploaded': function(up, file, info) {
-
-
$('#pickfiles').prop('disabled', false).html('上传图片');
-
var res = JSON.parse(info);
-
imgUrl = up.getOption('domain') + res.key;
-
refresh(imgUrl);
-
},
-
'Error': function(up, err, errTip) {
-
$('#pickfiles').prop('disabled', false).html('上传图片');
-
}
-
}
-
});
图片处理
图片处理的过程非常简单,就是将我们的图片处理 fop
以及对应的参数拼接在图片地址后面即可。基本不需要用到 SDK 的功能,当然你也可以使用我们的 jssdk 进行图片地址和处理参数的拼接。 比如一个图片:http://qiniuphotos.qiniudn.com/gogopher.jpg,现在我们对这个图片进行200x200的等比缩放,然后再进行居中剪裁,可以使用imageView2
的模式1,最终得到的图片:http://qiniuphotos.qiniudn.com/gogopher.jpg?imageView2/1/w/200/h/200。
更多处理规格请参考图片基本处理 (imageView2)文档。
图片信息
- 图片基本信息,只需要在图片外链后面拼接上
?imageInfo
,http://qiniuphotos.qiniudn.com/gogopher.jpg?imageInfo:
-
{
-
format: "jpeg",
-
width: 640,
-
height: 427,
-
colorModel: "ycbcr",
-
orientation: "Top-left"
-
}
- 图片平均色,只需要在图片的外链后面拼接上
?imageAve
,http://qiniuphotos.qiniudn.com/gogopher.jpg?imageAve:
-
{
-
RGB: "0x85694d"
-
}
- 图片 Exif 信息,只需要在图片的外链后面拼接上
?exif
,http://qiniuphotos.qiniudn.com/gogopher.jpg?exif:
-
{
-
ApertureValue: {
-
val: "5.00 EV (f/5.7)",
-
type: 5
-
},
-
ColorSpace: {
-
val: "sRGB",
-
type: 3
-
},
-
ComponentsConfiguration: {
-
val: "- - - -",
-
type: 7
-
},
-
...
-
}
自定义数据处理功能
除了我们官方提供的强大图片处理功能外,我们的自定义数据处理平台还提供了强大的第三方数据处理服务。在本例中我们就来尝试一下图片鉴黄服务(nrop)。
要使用 NROP 功能,用户需要先在管理平台上的第三方数据处理开启本功能。开启后使用方式与一般的数据处理指令完全一致(比如获取 EXIF 信息的接口),仅需要使用带 nrop 参数的 GET 请求即可,返回的 HTTP 响应内容为一个包含鉴定结果的 JSON 字符串。示例代码如下:
http://qiniuphotos.qiniudn.com/gogopher.jpg?nrop
-
{
-
statistic: [
-
0,
-
0,
-
1
-
],
-
reviewCount: 0,
-
fileList: [{
-
rate: 0.9946920275688171, // 介于0-1间的浮点数,表示该图像被识别为某个分类的概率值,概率越高、机器越肯定
-
label: 2, // 0:色情; 1:性感; 2:正常
-
name: "739a77baf4ff2d5eae5fe56602fc0cbe/gogopher.jpg",
-
review: false //是否需要人工复审该图片,鉴黄服务是否对结果确定(true:不确定,false:确定)
-
}],
-
nonce: "0.5508577267173678",
-
timestamp: 1437903830,
-
code: 0,
-
message: "success"
-
}
我们提供的完整示例已经包含了对 NROP 的调用,以及对广告的鉴定,您可以体验一下效果。
小结
这个示例结束后,相信你已经比较了解我们的平台是如何支持图片内容的编辑,基本上这些动作都只是一个简单的 GET 请求即可完成,甚至都不需要依赖于 SDK。您可以查看本示例的在线演示,或查看和下载本示例的完整源代码。
音视频处理
用一个示例详细讲解七牛云对音视频和流媒体格式的强大支持。该示例的目标为完成以下动作:
- 支持断点续传的大文件上传,因为视频文件通常比较大,难以用单次 POST 请求完成上传;
- 支持上传后异步执行的自动转码动作,生成若干不同规格的目标视频,分别适合在手机和电脑上播放;
- 将文件转换为 HLS 格式存放,以支持边下载边播放的效果;
- 为视频打上一个图片水印;
- 统一从每一个视频文件抽取一个固定时间点的画面作为预览图片;
- 在网页播放器中播放生成的视频文件;
我们选用 plupload 作为我们的上传控件,并使用 videojs 作为我们的网页播放器。其他采用的技术与之前的示例一致,主要是 Bootstrap 和 Smarty 。因为文件为客户端直传,因此我们也需要提供一个回调服务,以便于接收上传和转码这些异步任务的完成事件。为了简单起见,该示例就不再实现一个独立的业务数据库了,直接从目标存储空间获取文件信息。
大文件上传
因为大文件上传必须在浏览器端进行,因此我们就不演示如何用 PHP SDK 做断点续上传了。网页端的大文件上传我们可以用定制版本的 plupload 来支持。 具体可以查看我们的jssdk。
上传后自动转码
我们可以通过设置上传策略来通知云存储服务在上传完成后自动发起一个异步的任务。上传策略在调用上传接口时作为参数传入。 这里的转码过程需要支持转为 HLS 格式,并且在转码后打上视频水印。具体生成上传策略的代码为:
-
$bucket = Config::BUCKET_NAME;
-
$auth = new Auth(Config::AK, Config::SK);
-
-
$wmImg = Qiniu\base64_urlSafeEncode('http://rwxf.qiniudn.com/logo-s.png');
-
$pfopOps = "avthumb/m3u8/noDomain/1/wmImage/$wmImg";
-
$policy = array(
-
'persistentOps' => $pfopOps,
-
'persistentNotifyUrl' => 'http://<your_notify_url>',
-
);
-
-
$upToken = $auth->uploadToken($bucket, null, 3600, $policy);
-
-
echo json_encode(array('uptoken' => $upToken));
抽取视频截图
我们可以从上传视频中截取固定时间点的帧,以作为这些视频的封面图片。视频截图如下所示:
-
http://<your_uploaded_video>?vframe/jpg/offset/5
视频浏览与播放
在完成以上工作后,接下来的工作就简单了。我们可以生成一个列表网页,每个列表项都是一张视频截图,点击视频截图会弹出一个播放面板。因为类似的前端代码已经在本文档展示过,这里就不再多展示一次。
小结
视频的示例就到这里完成了。您可以直接查看本示例的在线演示,或下载和查看本示例的完整源代码。
需要注意的是,我们这边使用了videojs的开源播放器, 世面上还有其他比较优秀的播放器,具体你可以参考播放器推荐。
另外,对于视频内容,CDN 的选择会是一个影响视频播放是否能够足够流畅的关键因素。在融合 CDN 管理平台会给列出的可用 CDN 线路标注是否适用于视频加速,请合理选择。
文件上传
上传流程
为了尽可能地改善终端用户的上传体验,七牛云存储首创了客户端直传功能。更多信息请参阅业务流程。
上传代码:
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
// 引入鉴权类
-
use Qiniu\Auth;
-
-
// 引入上传类
-
use Qiniu\Storage\UploadManager;
-
-
// 需要填写你的 Access Key 和 Secret Key
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
-
// 构建鉴权对象
-
$auth = new Auth($accessKey, $secretKey);
-
-
// 要上传的空间
-
$bucket = 'Bucket_Name';
-
-
// 生成上传 Token
-
$token = $auth->uploadToken($bucket);
-
-
// 要上传文件的本地路径
-
$filePath = './php-logo.png';
-
-
// 上传到七牛后保存的文件名
-
$key = 'my-php-logo.png';
-
-
// 初始化 UploadManager 对象并进行文件的上传
-
$uploadMgr = new UploadManager();
-
-
// 调用 UploadManager 的 putFile 方法进行文件的上传
-
list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath);
-
echo "\n====> putFile result: \n";
-
if ($err !== null) {
-
var_dump($err);
-
} else {
-
var_dump($ret);
-
}
上传&回调
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Storage\UploadManager;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
$auth = new Auth($accessKey, $secretKey);
-
-
$bucket = 'Bucket_Name';
-
// 上传文件到七牛后, 七牛将文件名和文件大小回调给业务服务器
-
-
$policy = array(
-
'callbackUrl' => 'http://your.domain.com/callback.php',
-
'callbackBody' => 'filename=$(fname)&filesize=$(fsize)'
-
);
-
$uptoken = $auth->uploadToken($bucket, null, 3600, $policy);
-
-
//上传文件的本地路径
-
$filePath = './php-logo.png';
-
-
$uploadMgr = new UploadManager();
-
-
list($ret, $err) = $uploadMgr->putFile($uptoken, null, $filePath);
-
echo "\n====> putFile result: \n";
-
if ($err !== null) {
-
var_dump($err);
-
} else {
-
var_dump($ret);
-
}
上传&预转持续化
以视频转码为例:
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Storage\UploadManager;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
$auth = new Auth($accessKey, $secretKey);
-
-
$bucket = 'Bucket_Name';
-
-
//转码时使用的队列名称
-
$pipeline = 'abc';
-
-
//要进行转码的转码操作
-
$fops = "avthumb/mp4/s/640x360/vb/1.25m";
-
-
//可以对转码后的文件进行使用saveas参数自定义命名,当然也可以不指定文件会默认命名并保存在当间
-
$savekey = Qiniu\base64_urlSafeEncode('目标Bucket_Name:自定义文件key');
-
$fops = $fops.'|saveas/'.$savekey;
-
-
$policy = array(
-
'persistentOps' => $fops,
-
'persistentPipeline' => $pipeline
-
);
-
$uptoken = $auth->uploadToken($bucket, null, 3600, $policy);
-
-
//上传文件的本地路径
-
$filePath = './php-logo.png';
-
-
$uploadMgr = new UploadManager();
-
-
list($ret, $err) = $uploadMgr->putFile($uptoken, null, $filePath);
-
echo "\n====> putFile result: \n";
-
if ($err !== null) {
-
var_dump($err);
-
} else {
-
var_dump($ret);
-
}
队列 (pipeline) 请参阅[创建私有队列][mpsHref];转码操作具体参数请参阅音视频转码;saveas 请参阅处理结果另存。
Tips:上面的 Demo 只是针对视频转码功能,如果您需要使用比如音视频切片、视频截图、视频拼接等功能只需要修改上面 fops 后面的参数即可,如:$fops = vframe/jpg/offset/1/w/480/h/360/rotate/90 就表示视频截图了。
可以看到上传成功后的行为主要是由上传凭证中的上传策略来指定。其中 上传策略可以指定的行为不止这些,具体请参阅上传策略。等文件上传成功后就会向你设置的 callbackUrl 发起回调,你的服务器要接受并处理这个回调信息,并返回合法json数据到七牛服务器,七牛将接收到的 json 数据返回给客户端。下面我们给一个例子来看怎么接收回调信息,并验证该回调是否来自七牛:
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
$auth = new Auth($accessKey, $secretKey);
-
-
//获取回调的body信息
-
$callbackBody = file_get_contents('php://input');
-
-
//回调的contentType
-
$contentType = 'application/x-www-form-urlencoded';
-
-
//回调的签名信息,可以验证该回调是否来自七牛
-
$authorization = $_SERVER['HTTP_AUTHORIZATION'];
-
-
//七牛回调的url,具体可以参考
-
$url = 'http://your.domain.com/callback.php';
-
-
$isQiniuCallback = $auth->verifyCallback($contentType, $authorization, $url, $callbackBody);
-
-
if ($isQiniuCallback) {
-
$resp = array('ret' => 'success');
-
} else {
-
$resp = array('ret' => 'failed');
-
}
-
-
echo json_encode($resp);
文件下载
-
-
require 'path_to_sdk/vendor/autoload.php';
-
-
// 引入鉴权类
-
use Qiniu\Auth;
-
-
// 需要填写你的 Access Key 和 Secret Key
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
-
// 构建鉴权对象
-
$auth = new Auth($accessKey, $secretKey);
-
-
//baseUrl构造成私有空间的域名/key的形式
-
$baseUrl = 'http://domain/key';
-
$authUrl = $auth->privateDownloadUrl($baseUrl);
-
echo $authUrl;
-
空间资源管理
获取文件信息
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Storage\BucketManager;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
-
//初始化Auth状态
-
$auth = new Auth($accessKey, $secretKey);
-
-
//初始化BucketManager
-
$bucketMgr = new BucketManager($auth);
-
-
//你要测试的空间, 并且这个key在你空间中存在
-
$bucket = 'Bucket_Name';
-
$key = 'php-logo.png';
-
-
//获取文件的状态信息
-
list($ret, $err) = $bucketMgr->stat($bucket, $key);
-
echo "\n====> $key stat : \n";
-
if ($err !== null) {
-
var_dump($err);
-
} else {
-
var_dump($ret);
-
}
移动单个文件
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Storage\BucketManager;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
-
//初始化Auth状态
-
$auth = new Auth($accessKey, $secretKey);
-
-
//初始化BucketManager
-
$bucketMgr = new BucketManager($auth);
-
-
//你要测试的空间, 并且这个key在你空间中存在
-
$bucket = 'Bucket_Name';
-
$key = 'php-logo.png';
-
-
//将文件从文件$key移动到文件$key2。可以在不同bucket移动
-
$key2 = 'php-logo2.png';
-
$err = $bucketMgr->move($bucket, $key, $bucket, $key2);
-
echo "\n====> move $key to $key2 : \n";
-
if ($err !== null) {
-
var_dump($err);
-
} else {
-
echo "Success!";
-
}
复制单个文件
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Storage\BucketManager;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
-
//初始化Auth状态
-
$auth = new Auth($accessKey, $secretKey);
-
-
//初始化BucketManager
-
$bucketMgr = new BucketManager($auth);
-
-
//你要测试的空间, 并且这个key在你空间中存在
-
$bucket = 'Bucket_Name';
-
$key = 'php-logo.png';
-
-
//将文件从文件$key复制到文件$key2。可以在不同bucket复制
-
$key2 = 'php-logo2.png';
-
$err = $bucketMgr->copy($bucket, $key, $bucket, $key2);
-
echo "\n====> copy $key to $key2 : \n";
-
if ($err !== null) {
-
var_dump($err);
-
} else {
-
echo "Success!";
-
}
删除单个文件
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Storage\BucketManager;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
-
//初始化Auth状态
-
$auth = new Auth($accessKey, $secretKey);
-
-
//初始化BucketManager
-
$bucketMgr = new BucketManager($auth);
-
-
//你要测试的空间, 并且这个key在你空间中存在
-
$bucket = 'Bucket_Name';
-
$key = 'php-logo.png';
-
-
//删除$bucket 中的文件 $key
-
$err = $bucketMgr->delete($bucket, $key);
-
echo "\n====> delete $key : \n";
-
if ($err !== null) {
-
var_dump($err);
-
} else {
-
echo "Success!";
-
}
列举空间中文件
-
-
require_once __DIR__ . '/../autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Storage\BucketManager;
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
$auth = new Auth($accessKey, $secretKey);
-
$bucketMgr = new BucketManager($auth);
-
-
// 要列取的空间名称
-
$bucket = 'Bucket_Name';
-
-
// 要列取文件的公共前缀
-
$prefix = '';
-
-
$marker = '';
-
$limit = 3;
-
-
list($iterms, $marker, $err) = $bucketMgr->listFiles($bucket, $prefix, $marker, $limit);
-
if ($err !== null) {
-
echo "\n====> list file err: \n";
-
var_dump($err);
-
} else {
-
echo "Marker: $marker\n";
-
echo "\nList Iterms====>\n";
-
var_dump($iterms);
-
}
触发持久化操作
以视频转码为例:
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Auth;
-
use Qiniu\Processing\PersistentFop;
-
-
//对已经上传到七牛的视频发起异步转码操作
-
-
$accessKey = 'Access_Key';
-
$secretKey = 'Secret_Key';
-
$auth = new Auth($accessKey, $secretKey);
-
-
//要转码的文件所在的空间和文件名
-
$bucket = 'Bucket_Name';
-
$key = '1.mp4';
-
-
//转码是使用的队列名称
-
$pipeline = 'abc';
-
$pfop = new PersistentFop($auth, $bucket, $pipeline);
-
-
//要进行转码的转码操作
-
$fops = "avthumb/mp4/s/640x360/vb/1.25m";
-
-
list($id, $err) = $pfop->execute($key, $fops);
-
echo "\n====> pfop avthumb result: \n";
-
if ($err != null) {
-
var_dump($err);
-
} else {
-
echo "PersistentFop Id: $id\n";
-
}
-
-
//查询转码的进度和状态
-
list($ret, $err) = $pfop->status($id);
-
echo "\n====> pfop avthumb status: \n";
-
if ($err != null) {
-
var_dump($err);
-
} else {
-
var_dump($ret);
-
}
Tips:上面的Demo只是针对视频转码功能,如果您需要使用比如音视频切片、视频截图、视频拼接等功能只需要修改上面 fops 后面的参数即可,如:$fops = vframe/jpg/offset/1/w/480/h/360/rotate/90 就表示视频截图了。
通过上面的代码我们就可以触发将存储在空间中的视频从 mp4
格式转换成 m3u8
格式,并设置分辨率和视频码率。 但这个只是将转码这个耗时的操作提交到队列中,要想知道转码操作现在的状态,需要根据返回的 persitentId 进行查询。
-
-
require_once 'path_to_sdk/vendor/autoload.php';
-
-
use Qiniu\Processing\PersistentFop;
-
-
// 触发持久化处理后返回的 Id
-
$persistentId = 'z0.564d5f977823de48a85ece59';
-
-
// 通过persistentId查询该 触发持久化处理的状态
-
$status = PersistentFop::status($persistentId);
-
-
var_dump($status);
如果您不方便持续轮询每个异步处理的进度和状态,七牛可以异步处理完成后通知您们的业务服务器。 这样就需要您在视频转码的例子中,初始化 PersistentFop 时添加上 notifyUrl 来通知您们的业务服务器:
-
//你业务服务器的地址
-
$notifyUrl = 'http://your.bizhost.com/notify.php';
-
$pfop = new PersistentFop($auth, $bucket, $pipeline, $notifyUrl);
您的业务服务器需要接收来自七牛的通知:
-
-
-
// 获取notify通知的body信息
-
$notifyBody = file_get_contents('php://input');
-
-
// 业务服务器处理通知信息, 这里直接打印在log中
-
error_log($notifyBody);
我们以视频转码为例子,完整介绍了触发持久化的整个流程。触发持久化本质上是:使用异步队列的方式来处理已上传到七牛的数据资源并将处理的结果持久化到七牛。虽然可以使用触发持久化的方式处理七牛所有的数据处理,但是图片处理等可以同步处理的,建议使用同步处理。关于音视频处理,比如音视频转码,音视频切片,音视频拼接等较耗时的操作必须使用触发持久化方式,或者在上传凭证中指定处理参数的预转持久化操作。
API参考手册