粥里有勺糖

vuePress-theme-reco 粥里有勺糖    2018 - 2023
粥里有勺糖 粥里有勺糖

Choose mode

  • dark
  • auto
  • light
关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
author-avatar

粥里有勺糖

285

文章

40

标签

关于我
备战春秋
  • 心得总结
  • 校招考点汇总
  • 面经汇总
  • 复习自查
技术笔记
  • 技术教程
  • 模板工程
  • 源码学习
  • 技术概念
  • 个人作品
  • 学习笔记
计算机基础
  • 算法与数据结构
  • 操作系统
  • 计算机网络
  • 设计模式
  • 剑指offer
大前端
  • javascript
  • vue
  • html
  • css
  • 🌏浏览器专题
  • Web性能优化
  • regexp
  • node
面试
  • 问解
  • javascript
  • css
  • 手撕代码
  • 性能优化
  • 综合问题
  • 面经汇总
  • 小程序
手撕代码
  • 数据结构与算法
  • javascript
  • css
个人站点
  • GitHub (opens new window)
  • 博客园 (opens new window)
  • 掘金 (opens new window)
线上作品
  • 轻取(文件收集) (opens new window)
  • 个人图床 (opens new window)
  • 考勤小程序 (opens new window)
  • 时光恋人 (opens new window)
  • 在线简历生成 (opens new window)
留言板
Github (opens new window)
  • devlearn

    • 开发教程
    • 实践:利用ArrayBuffer实现预览指定目录下的所有文件的内容
    • 在linux-deepin上使用deepin-wine5完美运行腾讯会议/QQ/微信等此类应用
    • eslint插件开发教程
    • ServerLess之云函数实践-天气API
    • 移动端阻止弹窗下层页面被滑动
    • 小技巧:for of中获取index
    • Git常用的一些基本操作
    • 向页面注入js实现为图片和文字元素添加透明蒙层
    • 实践:使用jsencrypt配合axios实现非对称加密传输的数据
    • 封装dotenv库实现类似Vite加载环境变量的行为
    • 30行代码实现合并指定目录下的所有文件的内容
    • 马上中秋了,把鼠标指针变为小玉兔
    • Node中require与fs.readFile读取JSON文件的对比
    • 使用免费的七牛云OSS(10G)搭建个人的在线图床
    • 分享封装的一些七牛云OSS操作方法
    • 本地配置SSH免密远程登录服务器
    • 工具方法汇总
    • 腾讯云Serverless实践-Node.js服务部署
    • 腾讯云Serverless实践-静态网站托管
    • 为什么'\x1B'.length === 1?\x与\u知识延伸
    • Vite插件开发纪实:vite-plugin-monitor(上)
    • Vite插件开发纪实:vite-plugin-monitor(中)
    • Vite插件开发纪实:vite-plugin-monitor(下)
    • 解决Vite-React项目中js使用jsx语法报错的问题
    • webpack 项目接入Vite的通用方案介绍
    • webpack 项目接入Vite的通用方案介绍-草稿
    • 优雅的处理挂载window上的函数可能不存在的情况
    • Mac上抓包秒通关羊了个羊

分享封装的一些七牛云OSS操作方法

vuePress-theme-reco 粥里有勺糖    2018 - 2023

分享封装的一些七牛云OSS操作方法

粥里有勺糖 2021-06-03 技术笔记技术教程

# 分享封装的一些七牛云OSS操作方法

# 前言

最近在用七牛云对象存储服务 (opens new window)做了一个小应用 (opens new window)

对一些使用频率较高的方法进行了封装,和大家分享一下,帮助大家开发提效

# 准备工作

项目中安装qiniu依赖

npm i qiniu
1

鉴权代码,通过accessKey与secretKey鉴权

const qiniu = require('qiniu')
const qiniuConfig = {
    accessKey: process.env.qiniu_accessKey,
    secretKey: process.env.qiniu_secretKey
}
const mac = new qiniu.auth.digest.Mac(qiniuConfig.accessKey, qiniuConfig.secretKey)
1
2
3
4
5
6

公共参数

// 空间名称
const bucket = 'bucket'
// 空间绑定的域名
const privateBucketDomain = 'http://example.com'
1
2
3
4

# 方法汇总

# 上传凭证获取

凭证默认1小时有效,客户端需要此凭证进行上传鉴权

function getUploadToken(): string {
  const putPolicy = new qiniu.rs.PutPolicy({
    scope: bucket,
    // expires: 3600,
    // returnBody: '{"key":"$(key)","hash":"$(etag)","fsize":$(fsize),"bucket":"$(bucket)","name":"$(x:name)"}'
  })
  return putPolicy.uploadToken(mac)
}
1
2
3
4
5
6
7
8

通过returnBody可以自定义上传成功的响应

# 客户端上传

注:这里客户端是指浏览器

安装依赖

npm install qiniu-js
1

客户端直传,封装了三个回调:

  • success: 上传成功
  • error: 上传出错
  • process: 上传中
import * as qiniu from 'qiniu-js'

/**
 * 七牛云上传
 * @param token 上传凭证
 * @param file 待上传的文件
 * @param key 最终存储在OSS中资源的key
 * @param options 成功/失败/上传中回调
 */
function qiniuUpload(token, file, key, options) {
  const observable = qiniu.upload(file, key, token)
  const { success, error, process } = options || {}
  const subscription = observable.subscribe({
    // subscription.close() // 取消上传
    next(res) {
      const { total: { percent } } = res
      if (process) {
        process(percent.toFixed(2), res, subscription)
      }
    },
    error(err) {
      if (error) {
        error(err, subscription)
      }
    },
    complete(res) {
      if (success) {
        success(res, subscription)
      }
    },
  })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
调用示例
qiniuUpload(uploadToken, file, key, {
  success(data) {
    console.log('上传成功')
  },
  process(per) {
    // 打印上传进度
    console.log(~~(per))
  },
})
1
2
3
4
5
6
7
8
9

# 获取OSS文件的下载链接

const getDeadline = () => {
    // 12小时过期
    return Math.floor(Date.now() / 1000) + 3600 * 12
}
/**
 * 获取OSS上文件的下载链接
 * @param key 文件的key
 * @param expiredTime 链接过期的具体时间
 */
function createDownloadUrl(key, expiredTime = getDeadline()) {
    const config = new qiniu.conf.Config()
    const bucketManager = new qiniu.rs.BucketManager(mac, config)
    return bucketManager.privateDownloadUrl(privateBucketDomain, key, expiredTime)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 单个删除指定资源

function deleteSourceByKey(key){
  const config = new qiniu.conf.Config()
  const bucketManager = new qiniu.rs.BucketManager(mac, config)
  bucketManager.delete(bucket, key, (err) => {
    if (err) {
      console.log('------删除失败-------')
      console.log(key)
      console.log(err)
    }
  })
}
1
2
3
4
5
6
7
8
9
10
11

# 批量删除指定资源

function batchDeleteFiles(keys) {
  const config = new qiniu.conf.Config()
  const delOptions = keys.map((k) => qiniu.rs.deleteOp(bucket, k))
  const bucketManager = new qiniu.rs.BucketManager(mac, config)
  bucketManager.batch(delOptions, (err, respBody, respInfo) => {
    if (err) {
      console.log(err)
    } else {
      // 200 is success, 298 is part success
      if (parseInt(`${respInfo.statusCode / 100}`, 10) === 2) {
        respBody.forEach((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)
      }
    }
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 删除指定前缀资源

function deleteFilesBuPrefix(prefix){
  const config = new qiniu.conf.Config()
  const bucketManager = new qiniu.rs.BucketManager(mac, config)
  bucketManager.listPrefix(bucket, {
    limit: 1000,
    prefix,
  }, (err, respBody) => {
    const files = respBody.items
    // 调用批量删除接口
    batchDeleteFiles(files.map((f) => f.key))
  })
}
1
2
3
4
5
6
7
8
9
10
11
12

# 判断资源是否存在

bucketManager.stat这个方法用于查看资源信息

function judgeFileIsExist(key){
  return new Promise((res) => {
    const config = new qiniu.conf.Config()
    const bucketManager = new qiniu.rs.BucketManager(mac, config)
    bucketManager.stat(bucket, key, (err, respBody, respInfo) => {
      if(respInfo?.statusCode){
        res(respInfo.statusCode !== 612)
      }else{
        res(false)
      }
    })
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 批量查询文件信息

export function batchFileStatus(keys){
  return new Promise((resolve, reject) => {
    const statOperations = keys.map((k) => qiniu.rs.statOp(bucket, k))
    const config = new qiniu.conf.Config()
    const bucketManager = new qiniu.rs.BucketManager(mac, config)
    bucketManager.batch(statOperations, (err, respBody, respInfo) => {
      if (err) {
        console.log(err)
        // throw err;
      } else {
        // 200 is success, 298 is part success
        if (parseInt(`${respInfo.statusCode / 100}`) == 2) {
          resolve(respBody)
        } else {
          console.log(respInfo.statusCode)
          console.log(respBody)
        }
      }
    })
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 多个文件归档为压缩包

这个地方文档非常模糊,需要耗费不少时间试错,需要用到前面提到的:

  • 获取资源下载链接方法
  • 获取资源上传凭证方法

由于代码较长,为避免影响阅读,固进行折叠

资源归档方法

const path = require('path')
const qiniu = require('qiniu')
const { urlsafeBase64Encode } = qiniu.util

function getKeyInfo(key: string) {
  const { name, base, ext } = path.parse(key)
  return {
    name, base, ext,
  }
}
/**
 * 资源归档为zip
 * @param {string[]} keys 需要归档的资源
 * @param {string} zipName 压缩包名称 
 * @returns 
 */
function makeZipWithKeys(keys, zipName){
  return new Promise((res) => {
    const names = []
    const content = keys.map((key) => {
      // 拼接原始url
      // 链接加密并进行Base64编码,别名去除前缀目录。
      const keyInfo = getKeyInfo(key)
      const { name, ext } = keyInfo
      let { base } = keyInfo

      // 判断别名是否存在,存在则后缀+数字自增
      let i = 1
      while (names.includes(base)) {
        base = `${name}_${i}${ext}`
        i += 1
      }
      names.push(base)
      const safeUrl = `/url/${urlsafeBase64Encode(createDownloadUrl(key))}/alias/${urlsafeBase64Encode(base)}`
      return safeUrl
    }).join('\n')
    const config = new qiniu.conf.Config({ zone: qiniu.zone.Zone_z2 })
    const formUploader = new qiniu.form_up.FormUploader(config)
    const putExtra = new qiniu.form_up.PutExtra()
    const key = `${Date.now()}-${~~(Math.random() * 1000)}.txt`

    formUploader.put(getUploadToken(), key, content, putExtra, (respErr,
      respBody, respInfo) => {
      if (respErr) {
        throw respErr
      }
      if (respInfo.statusCode == 200) {
        const { key } = respBody
        // 执行压缩 设置压缩资源的保存路径
        const zipKey = urlsafeBase64Encode(`${bucket}:temp_package/${Date.now()}/${zipName}.zip`)

        const fops = `mkzip/4/encoding/${urlsafeBase64Encode('gbk')}|saveas/${zipKey}`
        const operManager = new qiniu.fop.OperationManager(mac, config)
        const pipeline = '' // 使用公共队列
        // 下行。不知用处
        const options = { force: false }
        operManager.pfop(bucket, key, [fops], pipeline, options, (err, respBody, respInfo) => {
          if (err) {
            throw err
          }
          if (respInfo.statusCode == 200) {
            // 可直接通过statusUrl查询处理状态
            const statusUrl = `http://api.qiniu.com/status/get/prefop?id=${respBody.persistentId}`
            console.log(statusUrl)
            // 这里只返回任务id,转由客户端发请求查询
            res(respBody.persistentId)
          } else {
            console.log(respInfo.statusCode)
            console.log(respBody)
          }
        })
      } else {
        console.log(respInfo.statusCode)
        console.log(respBody)
      }
    })
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
查看资源归档完成状态
/**
 * 查询Fop任务完成状态
 * @param {string} persistentId 
 * @returns 
 */
function checkFopTaskStatus(persistentId){
  const config = new qiniu.conf.Config()
  const operManager = new qiniu.fop.OperationManager(null, config)
  return new Promise((res) => {
    operManager.prefop(persistentId, (err, respBody, respInfo) => {
      if (err) {
        console.log(err)
        throw err
      }
      if (respInfo.statusCode == 200) {
        // 结构 ![图片](https://img.cdn.sugarat.top/mdImg/MTYxMjg0MTQyODQ1Mg==612841428452)
        const item = respBody.items[0]
        const { code, key } = item
        res({ code, key })
      } else {
        console.log(respInfo.statusCode)
        console.log(respBody)
      }
    })
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

归档完成后即可调用下载资源文件的方法

# 参考

  • 七牛云-Node.js SDK (opens new window)
Edit this page (opens new window)
Last Updated: 2022/5/15 12:46:34