添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
怕老婆的西装  ·  首頁 | 內壢高中 Moodle·  4 月前    · 
胆小的毛衣  ·  中国自由贸易区服务网·  5 月前    · 
刀枪不入的钥匙扣  ·  Kotlin 10. Kotlin ...·  8 月前    · 
鬼畜的雪糕  ·  DigitalJuice ...·  9 月前    · 
  • 开发指南
    1. 安全机制
      1. 上传策略
      2. 上传凭证
      3. 下载凭证
      4. 管理凭证
        1. 管理凭证历史文档
    2. 上传资源
      1. 表单上传
      2. 分片上传 v1 版
      3. 分片上传 v2 版
      4. 简单反馈
      5. 回调通知
      6. 303 重定向
      7. 异步数据预处理
      8. 自定义响应内容
      9. 变量
    3. 下载资源
      1. 公开资源下载
      2. 私有资源下载
      3. 防盗链
      4. 下载设置
      5. 高级下载
    4. 管理存储空间
      1. 绑定源站域名
      2. 访问控制
      3. 静态页面管理
      4. 空间备注
      5. 文件客户端缓存
      6. 重定向
      7. 空间标签
      8. 空间授权
      9. 跨域资源共享
      10. 生命周期管理
      11. 事件通知
      12. 镜像回源
      13. 跨区域同步
      14. 空间访问日志
      15. 删除空间
    5. 数据安全
      1. Referer 防盗链
      2. 对象锁定
    6. 数据处理
      1. 图片样式
      2. 转码样式
      3. 原图保护
    7. AWS S3 兼容
      1. 存储类型
      2. 服务域名
      3. 签名认证
      4. 临时安全凭证
      5. 兼容公共头
      6. 兼容 API
        1. 使用 Bucket Policy 授权
      7. 兼容工具示例
    8. 附录
  • 控制台指南
    1. 控制台概览
    2. 空间管理
      1. 新建空间
      2. 空间列表管理
      3. 空间概览
      4. 空间设置
        1. 设置访问控制
        2. 设置静态页面
        3. 设置空间备注
        4. 设置文件客户端缓存
        5. 设置重定向
        6. 设置标签管理
        7. 设置空间授权
        8. 设置Referer防盗链
        9. 设置跨域资源共享
        10. 设置生命周期
        11. 设置事件通知
        12. 设置镜像回源
        13. 设置对象锁定
        14. 设置空间日志
        15. 设置空间内容审核
        16. 删除存储空间
      5. 域名管理
        1. 设置自定义CDN加速域名
        2. 设置自定义源站域名
      6. 数据处理
        1. 设置图片样式
        2. 设置转码样式
        3. 设置原图保护
    3. 文件管理
      1. 上传文件
      2. 创建目录
      3. 搜索文件
      4. 修改文件名
      5. 修改文件类型
      6. 查看文件详情
      7. 设置文件元信息
      8. 下载文件
      9. 分享文件
      10. 刷新 CDN 缓存
      11. 移动文件
      12. 删除文件
      13. 删除目录
      14. 修改文件存储类型
      15. 解冻文件
      16. 任务中心
    4. 设置跨区域同步
    5. 统计分析
    6. 存储安全
      1. 内容审核
      2. 违规列表
  • API 文档
  • API 概览
  • HTTP Headers
  • 错误响应
  • 数据格式
  • Service 接口
    1. 获取 Bucket 列表
  • Bucket 接口
    1. 创建 Bucket
    2. 删除 Bucket
    3. 获取 Bucket 空间域名
    4. 设置 Bucket 镜像源
    5. 设置 Bucket 访问权限
    6. 设置空间标签
    7. 查询空间标签
    8. 删除空间标签
  • Object 接口
    1. 直传文件
    2. 分片上传 v1 版
      1. 创建块
      2. 上传片
      3. 创建文件
    3. 分片上传 v2 版
      1. 初始化任务
      2. 分块上传数据
      3. 完成文件上传
      4. 终止上传
      5. 列举已上传分片
    4. 资源列举
    5. 资源元信息查询
    6. 资源元信息修改
    7. 资源移动/重命名
    8. 资源复制
    9. 资源删除
    10. 批量操作
    11. 修改文件状态
    12. 修改文件存储类型
    13. 解冻归档/深度归档存储文件
    14. 修改文件过期删除时间
    15. 修改文件生命周期
    16. 镜像资源更新
    17. 异步第三方资源抓取
  • 数据统计接口
  • SDK 下载
  • Android SDK
    1. Android SDK历史文档
      1. Android SDK V7
  • C/C++ SDK
  • C# SDK
    1. C# SDK v7.2.15 文档
    2. C# SDK v7.0 文档
    3. C# SDK v6 文档
  • Flutter SDK
  • Go SDK
    1. Go SDK历史版本
  • Java SDK
    1. Java SDK 历史文档
  • JavaScript SDK
    1. JavaScript SDK历史文档2.x
    2. JavaScript SDK历史文档1.x
  • Node.js SDK
    1. Node.js SDK V6
  • Objective-C SDK
  • PHP SDK
    1. PHP SDK历史文档
  • Python SDK
  • Ruby SDK
  • Rust SDK
  • 图形化工具 Kodo Browser
  • 命令行工具(qshell)
  • 数据迁移工具(kodoimport)
  • 检测工具
    1. 上传测试页面
    2. 网络检测工具(qwebtest)
  • Qiniu Jenkins Plugin
  • 数据安全最佳实践
    1. 保障安全上传、下载、管理数据
    2. 降低因账号密码泄露带来的未授权访问风险
    3. 降低被恶意篡改数据的风险
    4. 防止恶意文件上传
    5. 降低被恶意访问、盗量的风险
    6. 降低或因误操作导致的数据丢失的风险
    7. 跟踪、追溯对数据的关键操作
    8. 敏感数据安全防护
  • 对象存储开源生态建设
    1. 使用 Hadoop 管理对象存储 Kodo
      1. 使用 Hadoop S3A 客户端管理对象存储 Kodo
      2. 使用 Hadoop-Kodo 插件管理对象存储 Kodo
    2. 使用 Terraform 管理对象存储 Kodo
    3. 使用 Rclone 命令行工具管理对象存储 Kodo
    4. 使用 Kubernetes CSI 挂载对象存储 Kodo
    5. 使用 Restic 备份工具对接对象存储 Kodo
  • 此 SDK 适用于 Node.js v4 及以上版本。使用此 SDK 构建您的网络应用程序,能让您以非常便捷的方式将数据安全地存储到 七牛云 上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构服务和应用,通过 七牛云 及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。

    Node.js SDK 属于 七牛 服务端SDK之一,主要有如下功能:

  • 提供生成客户端上传所需的上传凭证的功能
  • 提供文件从服务端直接上传 七牛 的功能
  • 提供对 七牛 空间中文件进行管理的功能
  • 提供对 七牛 空间中文件进行处理的功能
  • 提供七牛CDN相关的刷新,预取,日志功能
  • Node.js SDK 项目地址
  • Node.js SDK 发布地址
  • Node.js SDK 历史文档
  • 推荐使用 npm 来安装:

    $ npm install qiniu
    

    Node.js SDK 的所有功能,都需要合法的授权。授权凭证的签算需要七牛账号下的一对有效的Access KeySecret Key,这对密钥可以通过如下步骤获得:

  • 点击注册🔗开通七牛开发者帐号
  • 如果已有账号,直接登录七牛开发者后台,点击这里🔗查看 Access Key 和 Secret Key
  • 客户端上传凭证
  • 简单上传凭证
  • 覆盖上传凭证
  • 自定义上传回复凭证
  • 带回调业务服务器的凭证
  • 带数据处理的凭证
  • 带自定义参数的凭证
  • 综合上传凭证
  • 服务器直传
  • 构建配置类
  • 文件上传(表单方式)
  • 字节数组上传(表单方式)
  • 数据流上传(表单方式)
  • 文件分片上传(断点续传)
  • 数据流分片上传(断点续传)
  • 解析自定义回复内容
  • 业务服务器验证存储服务回调
  • 文件上传分为客户端上传(主要是指网页端和移动端等面向终端用户的场景)和服务端上传两种场景,具体可以参考文档业务流程

    服务端SDK在上传方面主要提供两种功能,一种是生成客户端上传所需要的上传凭证,另外一种是直接上传文件到云端。

    客户端上传凭证

    客户端(移动端或者Web端)上传文件的时候,需要从客户自己的业务服务器获取上传凭证,而这些上传凭证是通过服务端的SDK来生成的,然后通过客户自己的业务API分发给客户端使用。根据上传的业务需求不同,Node.js SDK支持丰富的上传凭证生成方式。

    创建各种上传凭证之前,我们需要定义好其中鉴权对象mac

    var accessKey = 'your access key';
    var secretKey = 'your secret key';
    var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
    

    简单上传的凭证

    最简单的上传凭证只需要AccessKeySecretKeyBucket就可以。

    var options = {
      scope: bucket,
    var putPolicy = new qiniu.rs.PutPolicy(options);
    var uploadToken=putPolicy.uploadToken(mac);
    

    默认情况下,在不指定上传凭证的有效时间情况下,默认有效期为1个小时。也可以自行指定上传凭证的有效期,例如:

    //自定义凭证有效期(示例2小时,expires单位为秒,为上传凭证的有效时间)
    var options = {
      scope: bucket,
      expires: 7200
    var putPolicy = new qiniu.rs.PutPolicy(options);
    var uploadToken=putPolicy.uploadToken(mac);
    

    覆盖上传的凭证

    覆盖上传除了需要简单上传所需要的信息之外,还需要想进行覆盖的文件名称,这个文件名称同时可是客户端上传代码中指定的文件名,两者必须一致。

    var keyToOverwrite = 'qiniu.mp4';
    var options = {
      scope: bucket + ":" + keyToOverwrite
    var putPolicy = new qiniu.rs.PutPolicy(options);
    var uploadToken=putPolicy.uploadToken(mac);
    

    自定义上传回复的凭证

    默认情况下,文件上传到存储之后,在没有设置returnBody或者回调相关的参数情况下,存储返回给上传端的回复格式为hashkey,例如:

    {"hash":"Ftgm-CkWePC9fzMBTRNmPMhGBcSV","key":"qiniu.jpg"}
    

    有时候我们希望能自定义这个返回的JSON格式的内容,可以通过设置returnBody参数来实现,在returnBody中,我们可以使用七牛支持的魔法变量自定义变量

    var options = {
      scope: bucket,
      returnBody: '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}'
    var putPolicy = new qiniu.rs.PutPolicy(options);
    var uploadToken=putPolicy.uploadToken(mac);
    

    则文件上传到存储之后,收到的回复内容如下:

    {"key":"qiniu.jpg","hash":"Ftgm-CkWePC9fzMBTRNmPMhGBcSV","bucket":"if-bc","fsize":39335,"name":"qiniu"}
    

    带回调业务服务器的凭证

    上面生成的自定义上传回复的上传凭证适用于上传端(无论是客户端还是服务端)和存储服务器之间进行直接交互的情况下。在客户端上传的场景之下,有时候客户端需要在文件上传到存储之后,从业务服务器获取相关的信息,这个时候就要用到存储的上传回调及相关回调参数的设置。

    var options = {
      scope: bucket,
      callbackUrl: 'http://api.example.com/qiniu/upload/callback',
      callbackBody: '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}',
      callbackBodyType: 'application/json'
    var putPolicy = new qiniu.rs.PutPolicy(options);
    var uploadToken=putPolicy.uploadToken(mac);
    

    在使用了上传回调的情况下,客户端收到的回复就是业务服务器响应存储服务的JSON格式内容。
    通常情况下,我们建议使用 application/json 格式来设置 callbackBody ,保持数据格式的统一性。实际情况下,callbackBody 也支持 application/x-www-form-urlencoded 格式来组织内容,这个主要看业务服务器在接收到 callbackBody 的内容时如何解析。例如:

    var options = {
      scope: bucket,
      callbackUrl: 'http://api.example.com/qiniu/upload/callback',
      callbackBody: 'key=$(key)&hash=$(etag)&bucket=$(bucket)&fsize=$(fsize)&name=$(x:name)'
    var putPolicy = new qiniu.rs.PutPolicy(options);
    var uploadToken=putPolicy.uploadToken(mac);
    

    带数据处理的凭证

    SDK 支持在文件上传到存储之后,立即对其进行多种指令的数据处理,这个只需要在生成的上传凭证中指定相关的处理参数即可。

    var saveMp4Entry = qiniu.util.urlsafeBase64Encode(bucket + ":avthumb_test_target.mp4");
    var saveJpgEntry = qiniu.util.urlsafeBase64Encode(bucket + ":vframe_test_target.jpg");
    //数据处理指令,支持多个指令
    var avthumbMp4Fop = "avthumb/mp4|saveas/" + saveMp4Entry;
    var vframeJpgFop = "vframe/jpg/offset/1|saveas/" + saveJpgEntry;
    var options = {
      scope: bucket,
      //将多个数据处理指令拼接起来
      persistentOps: avthumbMp4Fop + ";" + vframeJpgFop,
      //数据处理队列名称,必填
      persistentPipeline: "video-pipe",
      //数据处理完成结果通知地址
      persistentNotifyUrl: "http://api.example.com/qiniu/pfop/notify",
    var putPolicy = new qiniu.rs.PutPolicy(options);
    var uploadToken=putPolicy.uploadToken(mac);
    

    队列 pipeline 请参阅创建私有队列;转码操作具体参数请参阅音视频转码;saveas 请参阅处理结果另存

    带自定义参数的凭证

    SDK支持客户端上传文件的时候定义一些自定义参数,这些参数可以在 returnBodycallbackBody 里面和存储牛内置支持的魔法变量(即系统变量)通过相同的方式来引用。这些自定义的参数名称必须以 x: 开头。例如客户端上传的时候指定了自定义的参数x:namex:age分别是stringint类型。那么可以通过下面的方式引用:

    var options = {
      //其他上传策略参数...
      returnBody: '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)","age":$(x:age)}'
    
    var options = {
      //其他上传策略参数...
      callbackBody: '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)","age":$(x:age)}',
    

    综合上传凭证

    上面的生成上传凭证的方法,都是通过设置上传策略🔗相关的参数来支持的,这些参数可以通过不同的组合方式来满足不同的业务需求,可以灵活地组织你所需要的上传凭证。

    服务端直传

    服务端直传是指客户利用服务端SDK从服务端直接上传文件到存储,交互的双方一般都在机房里面,所以服务端可以自己生成上传凭证,然后利用SDK中的上传逻辑进行上传,最后从存储服务获取上传的结果,这个过程中由于双方都是业务服务器,所以很少利用到上传回调的功能,而是直接自定义returnBody来获取自定义的回复内容。

    构建配置类

    存储支持空间创建在不同的机房,在使用 Node.js SDK 中的FormUploaderResumeUploader上传文件之前,必须要构建一个上传用的config对象,在该对象中,可以指定空间对应的zone以及其他的一些影响上传的参数。

    var config = new qiniu.conf.Config();
    // 空间对应的机房
    config.zone = qiniu.zone.Zone_z0;
    // 是否使用https域名
    //config.useHttpsDomain = true;
    // 上传是否使用cdn加速
    //config.useCdnDomain = true;
    

    其中关于Zone对象和机房的关系如下:

    Zone对象

    文件上传(表单方式)

    最简单的就是上传本地文件,直接指定文件的完整路径即可上传。

    var localFile = "/Users/jemy/Documents/qiniu.mp4";
    var formUploader = new qiniu.form_up.FormUploader(config);
    var putExtra = new qiniu.form_up.PutExtra();
    var key='test.mp4';
    // 文件上传
    formUploader.putFile(uploadToken, key, localFile, putExtra, function(respErr,
      respBody, respInfo) {
      if (respErr) {
        throw respErr;
      if (respInfo.statusCode == 200) {
        console.log(respBody);
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    字节数组上传(表单方式)

    可以支持将内存中的字节数组上传到空间中。

    var formUploader = new qiniu.form_up.FormUploader(config);
    var putExtra = new qiniu.form_up.PutExtra();
    var key='test.txt';
    formUploader.put(uploadToken, key, "hello world", putExtra, function(respErr,
      respBody, respInfo) {
      if (respErr) {
        throw respErr;
      if (respInfo.statusCode == 200) {
        console.log(respBody);
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    数据流上传(表单方式)

    这里演示的是ReadableStream对象的上传。

    var formUploader = new qiniu.form_up.FormUploader(config);
    var putExtra = new qiniu.form_up.PutExtra();
    var readableStream = xxx; // 可读的流
    formUploader.putStream(uploadToken, key, readableStream, putExtra, function(respErr,
      respBody, respInfo) {
      if (respErr) {
        throw respErr;
      if (respInfo.statusCode == 200) {
        console.log(respBody);
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    文件分片上传(断点续传)

    可以选择分片上传版本,推荐上传时制定 putExtra.version = ‘v2’,表示 分片上传 v2 版,默认分片上传 v1 版,兼容历史情况。若需深入了解上传方式之间的区别,请参阅上传类型中 表单上传分片上传 v1 版分片上传 v2 版 接口说明。

    var localFile = "/Users/jemy/Documents/qiniu.mp4";
    var resumeUploader = new qiniu.resume_up.ResumeUploader(config);
    var putExtra = new qiniu.resume_up.PutExtra();
    // 扩展参数
    putExtra.params = {
      "x:name": "",
      "x:age": 27,
    putExtra.fname = 'testfile.mp4';
    // 如果指定了断点记录文件,那么下次会从指定的该文件尝试读取上次上传的进度,以实现断点续传
    putExtra.resumeRecordFile = 'progress.log';
    //分片上传可指定 version 字段,v2 表示分片上传 v2 , 可自定义分片大小,此处设为 6MB
    putExtra.version = 'v2'
    putExtra.partSize = 6 * 1024 * 1024
    var key = null;
    // 文件分片上传
    resumeUploader.putFile(uploadToken, key, localFile, putExtra, function(respErr,
      respBody, respInfo) {
      if (respErr) {
        throw respErr;
      if (respInfo.statusCode == 200) {
        console.log(respBody);
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    数据流分片上传(断点续传)

    这里演示的是ReadableStream对象的上传。

    var formUploader = new qiniu.form_up.FormUploader(config);
    var putExtra = new qiniu.form_up.PutExtra();
    var readableStream = xxx; // 可读的流
    // 如果指定了断点记录文件,那么下次会从指定的该文件尝试读取上次上传的进度,以实现断点续传
    putExtra.resumeRecordFile = 'progress.log';
    // 分片上传可指定 version 字段,v2 表示分片上传 v2 , 可自定义分片大小,此处设为 6MB
    putExtra.version = 'v2';
    putExtra.partSize = 6 * 1024 * 1024;
    formUploader.putStream(uploadToken, key, readableStream, putExtra, function(respErr,
      respBody, respInfo) {
      if (respErr) {
        throw respErr;
      if (respInfo.statusCode == 200) {
        console.log(respBody);
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    解析自定义回复内容

    有些情况下,存储返回给上传端的内容不是默认的hashkey形式,这种情况下,可能出现在自定义returnBody或者自定义了callbackBody的情况下,前者一般是服务端直传的场景,而后者则是接受上传回调的场景,这两种场景之下,都涉及到需要将自定义的回复进行内容解析,一般建议在交互过程中,都采用JSON的方式,这样处理起来方法比较一致,而且JSON的方法最通用,在 Node.js 里面处理JSON的回复相当地方便,基本上了解回复结构就可以处理,这里不再赘述。

    业务服务器验证存储服务回调

    在上传策略里面设置了上传回调相关参数的时候,存储服务在文件上传到服务器之后,会主动地向callbackUrl发送POST请求的回调,回调的内容为callbackBody模版所定义的内容,如果这个模版里面引用了魔法变量或者自定义变量,那么这些变量会被自动填充对应的值,然后在发送给业务服务器。

    业务服务器在收到来自存储服务的回调请求的时候,可以根据请求头部的Authorization字段来进行验证,查看该请求是否是来自存储服务的未经篡改的请求。

    Node.js SDK中提供了一个方法qiniu.util.isQiniuCallback来校验该头部是否合法:

    // 校验存储服务上传回调的Authorization
    // @param mac           AK&SK对象
    // @param requestURI   回调的URL中的requestURI
    // @param reqBody      请求Body,仅当请求的ContentType为
    //                     application/x-www-form-urlencoded时才需要传入该参数
    // @param callbackAuth 回调时请求的Authorization头部值
    exports.isQiniuCallback = function(mac, requestURI, reqBody, callbackAuth) {
      var auth = exports.generateAccessToken(mac, requestURI, reqBody);
      return auth === callbackAuth;
    

    文件下载分为公开空间的文件下载和私有空间的文件下载。

    对于公开空间,其访问的链接主要是将空间绑定的域名(可以是空间的默认域名或者是绑定的自定义域名)拼接上空间里面的文件名即可访问,标准情况下需要在拼接链接之前,将文件名进行urlencode以兼容不同的字符。

    var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
    var config = new qiniu.conf.Config();
    var bucketManager = new qiniu.rs.BucketManager(mac, config);
    var publicBucketDomain = 'http://if-pbl.qiniudn.com';
    // 公开空间访问链接
    var publicDownloadUrl = bucketManager.publicDownloadUrl(publicBucketDomain, key);
    console.log(publicDownloadUrl);
    

    对于私有空间,首先需要按照公开空间的文件访问方式构建对应的公开空间访问链接,然后再对这个链接进行私有授权签名。

    var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
    var config = new qiniu.conf.Config();
    var bucketManager = new qiniu.rs.BucketManager(mac, config);
    var privateBucketDomain = 'http://if-pri.qiniudn.com';
    var deadline = parseInt(Date.now() / 1000) + 3600; // 1小时过期
    var privateDownloadUrl = bucketManager.privateDownloadUrl(privateBucketDomain, key, deadline);
    

    资源管理包括的主要功能有:

  • 获取文件信息
  • 修改文件MimeType
  • 修改文件存储类型
  • 移动或重命名文件
  • 复制文件副本
  • 删除空间中的文件
  • 设置或更新文件生存时间
  • 获取指定前缀文件列表
  • 抓取网络资源到空间
  • 更新镜像存储空间中文件内容
  • 资源管理批量操作
  • 批量获取文件信息
  • 批量修改文件类型
  • 批量删除文件
  • 批量复制文件
  • 批量移动或重命名文件
  • 批量更新文件的有效期
  • 批量更新文件存储类型
  • 资源管理相关的操作首先要构建BucketManager对象:

    var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
    var config = new qiniu.conf.Config();
    //config.useHttpsDomain = true;
    config.zone = qiniu.zone.Zone_z0;
    var bucketManager = new qiniu.rs.BucketManager(mac, config);
    

    获取文件信息

    var bucket = "if-pbl";
    var key = "qiniux.mp4";
    bucketManager.stat(bucket, key, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        if (respInfo.statusCode == 200) {
          console.log(respBody.hash);
          console.log(respBody.fsize);
          console.log(respBody.mimeType);
          console.log(respBody.putTime);
          console.log(respBody.type);
        } else {
          console.log(respInfo.statusCode);
          console.log(respBody.error);
    

    修改文件MimeType

    var bucket = 'if-pbl';
    var key = 'qiniu.mp4';
    var newMime = 'video/x-mp4';
    bucketManager.changeMime(bucket, key, newMime, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        //200 is success
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    修改文件存储类型

    var bucket = 'if-pbl';
    var key = 'qiniu.mp4';
    //newType=0表示普通存储,newType为1表示低频存储
    var newType = 0;
    bucketManager.changeType(bucket, key, newType, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        //200 is success
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    移动或重命名文件

    移动操作本身支持移动文件到相同,不同空间中,在移动的同时也可以支持文件重命名。唯一的限制条件是,移动的源空间和目标空间必须在同一个机房。

    目标文件名

    move操作支持强制覆盖选项,即如果目标文件已存在,可以设置强制覆盖选项force来覆盖那个文件的内容。

    var srcBucket = "if-pbl";
    var srcKey = "qiniu.mp4";
    var destBucket = "if-pbl";
    var destKey = "qiniu_new.mp4";
    // 强制覆盖已有同名文件
    var options = {
      force: true
    bucketManager.move(srcBucket, srcKey, destBucket, destKey, options, function(
      err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        //200 is success
        console.log(respInfo.statusCode);
    

    复制文件副本

    文件的复制和文件移动其实操作一样,主要的区别是移动后源文件不存在了,而复制的结果是源文件还存在,只是多了一个新的文件副本。

    var srcBucket = "if-pbl";
    var srcKey = "qiniu.mp4";
    var destBucket = "if-pbl";
    var destKey = "qiniu_new_copy.mp4";
    // 强制覆盖已有同名文件
    var options = {
      force: true
    bucketManager.copy(srcBucket, srcKey, destBucket, destKey, options, function(
      err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        //200 is success
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    删除空间中的文件

    var bucket = "if-pbl";
    var key = "qiniu_new_copy.mp4";
    bucketManager.delete(bucket, key, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    设置或更新文件的生存时间

    可以给已经存在于空间中的文件设置文件生存时间,或者更新已设置了生存时间但尚未被删除的文件的新的生存时间。

    var bucket = "if-pbl";
    var key = "qiniu_new_copy.mp4";
    var days = 10;
    bucketManager.deleteAfterDays(bucket, key, days, function(err, respBody,
      respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    获取指定前缀的文件列表

    var bucket = 'if-pbl';
    // @param options 列举操作的可选参数
    //                prefix    列举的文件前缀
    //                marker    上一次列举返回的位置标记,作为本次列举的起点信息
    //                limit     每次返回的最大列举文件数量
    //                delimiter 指定目录分隔符
    var options = {
      limit: 10,
      prefix: 'images/',
    bucketManager.listPrefix(bucket, options, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        throw err;
      if (respInfo.statusCode == 200) {
        //如果这个nextMarker不为空,那么还有未列举完毕的文件列表,下次调用listPrefix的时候,
        //指定options里面的marker为这个值
        var nextMarker = respBody.marker;
        var commonPrefixes = respBody.commonPrefixes;
        console.log(nextMarker);
        console.log(commonPrefixes);
        var items = respBody.items;
        items.forEach(function(item) {
          console.log(item.key);
          // console.log(item.putTime);
          // console.log(item.hash);
          // console.log(item.fsize);
          // console.log(item.mimeType);
          // console.log(item.endUser);
          // console.log(item.type);
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    抓取网络资源到空间

    var resUrl = 'http://devtools.qiniu.com/qiniu.png';
    var bucket = "if-bc";
    var key = "qiniu.png";
    bucketManager.fetch(resUrl, bucket, key, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        if (respInfo.statusCode == 200) {
          console.log(respBody.key);
          console.log(respBody.hash);
          console.log(respBody.fsize);
          console.log(respBody.mimeType);
        } else {
          console.log(respInfo.statusCode);
          console.log(respBody);
    

    更新镜像空间中存储的文件内容

    var bucket = "if-pbl";
    var key = "qiniu.mp4";
    bucketManager.prefetch(bucket, key, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        //200 is success
        console.log(respInfo.statusCode);
    

    资源管理批量操作

    批量获取文件信息

    //每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
    var statOperations = [
      qiniu.rs.statOp(srcBucket, 'qiniu1.mp4'),
      qiniu.rs.statOp(srcBucket, 'qiniu2.mp4'),
      qiniu.rs.statOp(srcBucket, 'qiniu3.mp4'),
      qiniu.rs.statOp(srcBucket, 'qiniu4x.mp4'),
    bucketManager.batch(statOperations, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        // 200 is success, 298 is part success
        if (parseInt(respInfo.statusCode / 100) == 2) {
          respBody.forEach(function(item) {
            if (item.code == 200) {
              console.log(item.data.fsize + "\t" + item.data.hash + "\t" +
                item.data.mimeType + "\t" + item.data.putTime + "\t" +
                item.data.type);
            } else {
              console.log(item.code + "\t" + item.data.error);
        } else {
          console.log(respInfo.statusCode);
          console.log(respBody);
    

    批量修改文件类型

    //每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
    var chgmOperations = [
      qiniu.rs.changeMimeOp(srcBucket, 'qiniu1.mp4', 'video/x-mp4'),
      qiniu.rs.changeMimeOp(srcBucket, 'qiniu2.mp4', 'video/x-mp4'),
      qiniu.rs.changeMimeOp(srcBucket, 'qiniu3.mp4', 'video/x-mp4'),
      qiniu.rs.changeMimeOp(srcBucket, 'qiniu4.mp4', 'video/x-mp4'),
    bucketManager.batch(chgmOperations, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        // 200 is success, 298 is part success
        if (parseInt(respInfo.statusCode / 100) == 2) {
          respBody.forEach(function(item) {
            if (item.code == 200) {
              console.log("success");
            } else {
              console.log(item.code + "\t" + item.data.error);
        } else {
          console.log(respInfo.statusCode);
          console.log(respBody);
    

    批量删除文件

    //每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
    var deleteOperations = [
      qiniu.rs.deleteOp(srcBucket, 'qiniu1.mp4'),
      qiniu.rs.deleteOp(srcBucket, 'qiniu2.mp4'),
      qiniu.rs.deleteOp(srcBucket, 'qiniu3.mp4'),
      qiniu.rs.deleteOp(srcBucket, 'qiniu4x.mp4'),
    bucketManager.batch(deleteOperations, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        // 200 is success, 298 is part success
        if (parseInt(respInfo.statusCode / 100) == 2) {
          respBody.forEach(function(item) {
            if (item.code == 200) {
              console.log(item.code + "\tsuccess");
            } else {
              console.log(item.code + "\t" + item.data.error);
        } else {
          console.log(respInfo.deleteusCode);
          console.log(respBody);
    

    批量复制文件

    var srcBucket = 'if-pbl';
    var srcKey = 'qiniu.mp4';
    var destBucket = srcBucket;
    //每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
    var copyOperations = [
      qiniu.rs.copyOp(srcBucket, srcKey, destBucket, 'qiniu1.mp4'),
      qiniu.rs.copyOp(srcBucket, srcKey, destBucket, 'qiniu2.mp4'),
      qiniu.rs.copyOp(srcBucket, srcKey, destBucket, 'qiniu3.mp4'),
      qiniu.rs.copyOp(srcBucket, srcKey, destBucket, 'qiniu4.mp4'),
    bucketManager.batch(copyOperations, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        // 200 is success, 298 is part success
        if (parseInt(respInfo.statusCode / 100) == 2) {
          respBody.forEach(function(item) {
            if (item.code == 200) {
              console.log(item.code + "\tsuccess");
            } else {
              console.log(item.code + "\t" + item.data.error);
        } else {
          console.log(respInfo.deleteusCode);
          console.log(respBody);
    

    批量移动或重命名文件

    var srcBucket = 'if-pbl';
    var destBucket = srcBucket;
    //每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
    var moveOperations = [
      qiniu.rs.moveOp(srcBucket, 'qiniu1.mp4', destBucket, 'qiniu1_move.mp4'),
      qiniu.rs.moveOp(srcBucket, 'qiniu2.mp4', destBucket, 'qiniu2_move.mp4'),
      qiniu.rs.moveOp(srcBucket, 'qiniu3.mp4', destBucket, 'qiniu3_move.mp4'),
      qiniu.rs.moveOp(srcBucket, 'qiniu4.mp4', destBucket, 'qiniu4_move.mp4'),
    bucketManager.batch(moveOperations, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        // 200 is success, 298 is part success
        if (parseInt(respInfo.statusCode / 100) == 2) {
          respBody.forEach(function(item) {
            if (item.code == 200) {
              console.log(item.code + "\tsuccess");
            } else {
              console.log(item.code + "\t" + item.data.error);
        } else {
          console.log(respInfo.deleteusCode);
          console.log(respBody);
    

    批量更新文件的有效期

    var srcBucket = 'if-pbl';
    //每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
    var deleteAfterDaysOperations = [
      qiniu.rs.deleteAfterDaysOp(srcBucket, 'qiniu1.mp4', 10),
      qiniu.rs.deleteAfterDaysOp(srcBucket, 'qiniu2.mp4', 10),
      qiniu.rs.deleteAfterDaysOp(srcBucket, 'qiniu3.mp4', 10),
      qiniu.rs.deleteAfterDaysOp(srcBucket, 'qiniu4.mp4', 10),
    bucketManager.batch(deleteAfterDaysOperations, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        // 200 is success, 298 is part success
        if (parseInt(respInfo.statusCode / 100) == 2) {
          respBody.forEach(function(item) {
            if (item.code == 200) {
              console.log(item.code + "\tsuccess");
            } else {
              console.log(item.code + "\t" + item.data.error);
        } else {
          console.log(respInfo.statusCode);
          console.log(respBody);
    

    批量更新文件存储类型

    var srcBucket = 'if-pbl';
    //每个operations的数量不可以超过1000个,如果总数量超过1000,需要分批发送
    //type=0为普通存储,type=1为低频存储
    var changeTypeOperations = [
      qiniu.rs.changeTypeOp(srcBucket, 'qiniu1.mp4', 1),
      qiniu.rs.changeTypeOp(srcBucket, 'qiniu2.mp4', 1),
      qiniu.rs.changeTypeOp(srcBucket, 'qiniu3.mp4', 1),
      qiniu.rs.changeTypeOp(srcBucket, 'qiniu4.mp4', 1),
    bucketManager.batch(changeTypeOperations, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        //throw err;
      } else {
        // 200 is success, 298 is part success
        if (parseInt(respInfo.statusCode / 100) == 2) {
          respBody.forEach(function(item) {
            if (item.code == 200) {
              console.log("success");
            } else {
              console.log(item.code + "\t" + item.data.error);
        } else {
          console.log(respInfo.statusCode);
          console.log(respBody);
    

    持久化数据处理

    发送数据处理请求

    对于已经保存到七牛空间的文件,可以通过发送持久化的数据处理指令来进行处理,这些指令支持七牛官方提供的指令,也包括客户自己开发的自定义数据处理的指令。数据处理的结果还可以通过七牛主动通知的方式告知业务服务器。

    var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
    var config = new qiniu.conf.Config();
    //config.useHttpsDomain = true;
    config.zone = qiniu.zone.Zone_z1;
    var operManager = new qiniu.fop.OperationManager(mac, config);
    //处理指令集合
    var saveBucket = 'if-bc';
    var fops = [
      'avthumb/mp4/s/480x320/vb/150k|saveas/' + qiniu.util.urlsafeBase64Encode(saveBucket + ":qiniu_480x320.mp4"),
      'vframe/jpg/offset/10|saveas/' + qiniu.util.urlsafeBase64Encode(saveBucket + ":qiniu_frame1.jpg")
    var pipeline = 'jemy';
    var srcBucket = 'if-bc';
    var srcKey = 'qiniu.mp4';
    var options = {
      'notifyURL': 'http://api.example.com/pfop/callback',
      'force': false,
    //持久化数据处理返回的是任务的persistentId,可以根据这个id查询处理状态
    operManager.pfop(srcBucket, srcKey, fops, pipeline, options, function(err, respBody, respInfo) {
      if (err) {
        throw err;
      if (respInfo.statusCode == 200) {
        console.log(respBody.persistentId);
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    查询数据处理请求状态

    由于数据处理是异步处理,可以根据发送处理请求时返回的 persistentId 去查询任务的处理进度,如果在设置了persistentNotifyUrl 的情况下,直接业务服务器等待处理结果通知即可,如果需要主动查询,可以采用如下代码中的:

    var persistentId = 'na0.58df4eee92129336c2075195';
    var config = new qiniu.conf.Config();
    var operManager = new qiniu.fop.OperationManager(null, config);
    //持久化数据处理返回的是任务的persistentId,可以根据这个id查询处理状态
    operManager.prefop(persistentId, function(err, respBody, respInfo) {
      if (err) {
        console.log(err);
        throw err;
      if (respInfo.statusCode == 200) {
        console.log(respBody.inputBucket);
        console.log(respBody.inputKey);
        console.log(respBody.pipeline);
        console.log(respBody.reqid);
        respBody.items.forEach(function(item) {
          console.log(item.cmd);
          console.log(item.code);
          console.log(item.desc);
          console.log(item.hash);
          console.log(item.key);
      } else {
        console.log(respInfo.statusCode);
        console.log(respBody);
    

    CDN相关功能

  • 文件预取操作
  • 获取域名流量
  • 获取域名带宽
  • 获取日志下载链接
  • 构建时间戳防盗链访问链接
  • 在使用CDN相关功能之前,需要构建CdnManager对象:

    var accessKey = 'your access key';
    var secretKey = 'your secret key';
    var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
    var cdnManager = new qiniu.cdn.CdnManager(mac);
    
    //URL 列表
    var urlsToRefresh = [
      'http://if-pbl.qiniudn.com/nodejs.png',
      'http://if-pbl.qiniudn.com/qiniu.jpg'
    //刷新链接,单次请求链接不可以超过100个,如果超过,请分批发送请求
    cdnManager.refreshUrls(urlsToRefresh, function(err, respBody, respInfo) {
      if (err) {
        throw err;
      console.log(respInfo.statusCode);
      if (respInfo.statusCode == 200) {
        var jsonBody = JSON.parse(respBody);
        console.log(jsonBody.code);
        console.log(jsonBody.error);
        console.log(jsonBody.requestId);
        console.log(jsonBody.invalidUrls);
        console.log(jsonBody.invalidDirs);
        console.log(jsonBody.urlQuotaDay);
        console.log(jsonBody.urlSurplusDay);
        console.log(jsonBody.dirQuotaDay);
        console.log(jsonBody.dirSurplusDay);
    
    //DIR 列表
    var dirsToRefresh = [
      'http://if-pbl.qiniudn.com/examples/',
      'http://if-pbl.qiniudn.com/images/'
    //单次请求链接不可以超过10个,如果超过,请分批发送请求
    qiniu.cdn.refreshDirs(dirsToRefresh, function(err, respBody, respInfo) {
      if (err) {
        throw err;
      console.log(respInfo.statusCode);
      if (respInfo.statusCode == 200) {
        var jsonBody = JSON.parse(respBody);
        console.log(jsonBody.code);
        console.log(jsonBody.error);
        console.log(jsonBody.requestId);
        console.log(jsonBody.invalidUrls);
        console.log(jsonBody.invalidDirs);
        console.log(jsonBody.urlQuotaDay);
        console.log(jsonBody.urlSurplusDay);
        console.log(jsonBody.dirQuotaDay);
        console.log(jsonBody.dirSurplusDay);
    
    //URL 列表
    var urlsToPrefetch = [
      'http://if-pbl.qiniudn.com/nodejs.png',
      'http://if-pbl.qiniudn.com/qiniu.jpg'
    //预取链接,单次请求链接不可以超过100个,如果超过,请分批发送请求
    cdnManager.prefetchUrls(urlsToPrefetch, function(err, respBody, respInfo) {
      if (err) {
        throw err;
      console.log(respInfo.statusCode);
      if (respInfo.statusCode == 200) {
        var jsonBody = JSON.parse(respBody);
        console.log(jsonBody.code);
        console.log(jsonBody.error);
        console.log(jsonBody.requestId);
        console.log(jsonBody.invalidUrls);
        console.log(jsonBody.invalidDirs);
        console.log(jsonBody.urlQuotaDay);
        console.log(jsonBody.urlSurplusDay);
        console.log(jsonBody.dirQuotaDay);
        console.log(jsonBody.dirSurplusDay);
    

    获取域名流量

    //域名列表
    var domains = [
      'if-pbl.qiniudn.com',
      'qdisk.qiniudn.com'
    //指定日期
    var startDate = '2017-06-20';
    var endDate = '2017-06-22';
    var granularity = 'day';
    //获取域名流量
    cdnManager.getFluxData(startDate, endDate, granularity, domains, function(err,
      respBody, respInfo) {
      if (err) {
        throw err;
      console.log(respInfo.statusCode);
      if (respInfo.statusCode == 200) {
        var jsonBody = JSON.parse(respBody);
        var code = jsonBody.code;
        console.log(code);
        var tickTime = jsonBody.time;
        console.log(tickTime);
        var fluxData = jsonBody.data;
        domains.forEach(function(domain) {
          var fluxDataOfDomain = fluxData[domain];
          if (fluxDataOfDomain != null) {
            console.log("flux data for:" + domain);
            var fluxChina = fluxDataOfDomain["china"];
            var fluxOversea = fluxDataOfDomain["oversea"];
            console.log(fluxChina);
            console.log(fluxOversea);
          } else {
            console.log("no flux data for:" + domain);
          console.log("----------");
    

    获取域名带宽

    //域名列表
    var domains = [
      'if-pbl.qiniudn.com',
      'qdisk.qiniudn.com'
    //指定日期
    var startDate = '2017-06-20';
    var endDate = '2017-06-22';
    var granularity = 'day';
    //获取域名带宽
    cdnManager.getBandwidthData(startDate, endDate, granularity, domains, function(
      err, respBody, respInfo) {
      if (err) {
        console.log(err);
        throw err;
      console.log(respInfo.statusCode);
      if (respInfo.statusCode == 200) {
        var jsonBody = JSON.parse(respBody);
        var code = jsonBody.code;
        console.log(code);
        var tickTime = jsonBody.time;
        console.log(tickTime);
        var bandwidthData = jsonBody.data;
        domains.forEach(function(domain) {
          var bandwidthDataOfDomain = bandwidthData[domain];
          if (bandwidthDataOfDomain != null) {
            console.log("bandwidth data for:" + domain);
            var bandwidthChina = bandwidthDataOfDomain["china"];
            var bandwidthOversea = bandwidthDataOfDomain["oversea"];
            console.log(bandwidthChina);
            console.log(bandwidthOversea);
          } else {
            console.log("no bandwidth data for:" + domain);
          console.log("----------");
    

    获取日志下载链接

    //域名列表
    var domains = [
      'if-pbl.qiniudn.com',
      'qdisk.qiniudn.com'
    //指定日期
    var logDay = '2017-06-20';
    //获取域名日志
    cdnManager.getCdnLogList(domains, logDay, function(err, respBody, respInfo) {
      if (err) {
        throw err;
      console.log(respInfo.statusCode);
      if (respInfo.statusCode == 200) {
        var jsonBody = JSON.parse(respBody);
        var code = jsonBody.code;
        console.log(code);
        var logData = jsonBody.data;
        domains.forEach(function(domain) {
          console.log("log for domain: " + domain);
          var domainLogs = logData[domain];
          if (domainLogs != null) {
            domainLogs.forEach(function(logItem) {
              console.log(logItem.name);
              console.log(logItem.size);
              console.log(logItem.mtime);
              console.log(logItem.url);
            console.log("------------------");
    

    构建时间戳防盗链访问链接

    具体算法可以参考:时间戳防盗链

    var domain = 'http://sg.xiaohongshu.com';
    var fileName = 'github.png';
    //加密密钥
    var encryptKey = 'xxx';
    var query = {
      'name': 'qiniu',
      'location': 'shanghai'
    var deadline = parseInt(Date.now() / 1000) + 3600;
    var cdnManager = new qiniu.cdn.CdnManager(null);
    var finalUrl = cdnManager.createTimestampAntiLeechUrl(domain, fileName, query, encryptKey, deadline);
    console.log(finalUrl);
    

    API 参考

  • 存储 API 参考
  • 融合CDN API 参考
  • 官方数据处理 API 参考
  • Node.js SDK的callback保留了请求的错误信息,回复信息和头部信息,遇到问题时,可以都打印出来提交给我们排查问题。
  • API 的使用,可以参考我们为大家精心准备的使用实例
  • 如果您有任何关于我们文档或产品的建议和想法,欢迎您通过以下方式与我们互动讨论:

  • 技术论坛 - 在这里您可以和其他开发者愉快的讨论如何更好的使用七牛云服务
  • 提交工单 - 如果您的问题不适合在论坛讨论或希望及时解决,您也可以提交一个工单,我们的技术支持人员会第一时间回复您
  • 博客 - 这里会持续更新发布市场活动和技术分享文章
  • 创建您的特性分支 git checkout -b my-new-feature

    提交您的改动 git commit -am ‘Added some feature’

    将您的修改记录提交到远程 git 仓库 git push origin my-new-feature

    然后到 github 网站的该 git 远程仓库的 my-new-feature 分支下发起 Pull Request

    基于 MIT 协议发布:

  • www.opensource.org/licenses/MIT
  •