一看就懂:我是如何使用OSS提供的CDN服务的?

69 阅读1分钟

oss配置属性

module.exports = {
    region: 'oss-cn-wulanchabu',
    accessKeyId: 'LTAI5t8FUnVfkDQ1FqFmdHNU',
    accessKeySecret: 'z6s9djSAMYcaNZblxgrtikPBXppjMu',
    bucket: 'mohaixiao-avatar',
    accessUrl: 'https://file.mohaixiao.top/'
}
  • region

image.png

  • accessKeyId && accessKeySecret

image.png

  • bucket名字

image.png

  • accessUrl 申请的域名进行解析子域名并且绑定bucket的域名

image.png

配置OSS方法工具

const OSS = require('ali-oss')
const fs = require('fs')
const { resolve } = require('path')
const aliossConfig = require('../config/alioss')

class AliossTool {
  // 初始化阿里云实例
  static async getClient() {
    return new OSS({ ...aliossConfig })
  }
  // 拼接图片url上传阿里云oss
  static async uploadImagesToOSS(file) {
    console.log(file);
    const path =
      'user_file/' + file.originalname.split('.').shift() + '-' + Date.now() + '.' + file.mimetype.split('/').pop()
    const localPath = resolve(__dirname, `../tempImg/${file.filename}`)
    // 将图片url推送阿里云oss
    await AliossTool.putOSS(localPath, path)
    // 删除本地图片
    fs.unlinkSync(localPath)
    return aliossConfig.accessUrl + path
  }
  // 上传方法
  static async putOSS(img, uploadName) {
    const client = await AliossTool.getClient()
    try {
      await client.put(uploadName, img)
    } catch (err) {
      throw new Error('上传失败:', err)
    }
  }
}

module.exports = AliossTool

解读一下当前上面的代码

  1. 导入必要的模块和依赖,包括 ali-ossfs 和 path
  2. 导入阿里云 OSS 的配置文件 aliossConfig
  3. 创建一个名为 AliossTool 的类,其中包含了静态方法 getClientuploadImagesToOSS 和 putOSS
  4. getClient 方法用于初始化阿里云实例,返回一个 OSS 对象。
  5. uploadImagesToOSS 方法用于将图片上传到阿里云 OSS。它接受一个文件对象作为参数,并根据文件的属性生成一个目标路径 path
  6. 使用 resolve 方法获取本地图片文件的路径 localPath
  7. 调用 putOSS 方法将本地图片上传到阿里云 OSS。
  8. 使用 fs.unlinkSync 删除本地图片文件。
  9. 返回上传后的图片 URL,其中包括了阿里云 OSS 的访问 URL 前缀。
  10. putOSS 方法实现了将图片上传到阿里云 OSS 的逻辑。它首先调用 getClient 方法获取一个阿里云实例,然后使用实例的 put 方法将图片上传到指定的路径 uploadName
  11. 如果上传失败,会抛出一个包含错误信息的异常。

最后,通过 module.exports 导出了 AliossTool 类,以便在其他文件中使用该工具类。

上传图片的接口开发

www.npmjs.com/package/mul…

image.png

创建接口

http://127.0.0.1:8081/api/user/v1/update_img

上传插件配置

yarn add multer@1.4.5-lts.1
const multer = require('multer')
const upload = multer({ dest: 'tempImg/' })

个人头像修改

router.post('/update_img', upload.single('headImg'), UserController.update_img)

数据层逻辑开发

update_img: async (req) => {
  const url = await AliossTool.uploadImagesToOSS(req.file)
  if (!url) {
    return BackCode.buildError({ msg: '上传失败!' })
  }
  const user = SecretTool.jwtVerify(req.headers.authorization.split(' ').pop())
  const data = await DB.Account.update({ head_img: url }, { where: { id: user.id } })
  return data > 0 ? BackCode.buildSuccess() : BackCode.buildError({ msg: '上传失败!' })
}

req.file被处理为了一个对象

image.png

根据工具oss处理之后的path作为图片的名字也就是(下面加了子域名)

image.png

图片优化+缓存

本地图片CDN加速

/**
 * 阿里云OSS上传
 */
import OSS from 'ali-oss'
import * as fs from 'node:fs'
import * as path from 'node:path'

const prefix = 'images' // 阿里云上传后的文件夹
const toUpload = ['.images'] // 本地应上传到阿里云的文件夹

// OSS相关的权限配置和bucket地址
const client = new OSS({
  region: 'oss-cn-shenzhen',
  accessKeyId: 'LTAI5tMizqXWkZuBM8woemdu',
  accessKeySecret: 'fmZvd3ycsxtvKnB8HOW6l9IkR8gZX8',
  bucket: 'xdclass-cdn-images'
})

/**
 * 上传文件到阿里云OSS
 */
async function uploadImagesToOss() {
  // 判断本地目录是否存在的容错处理
  if (isLocalDirExists().length < 0) {
    throw new Error('本地打包目录不存在.')
  }

  // 先删除阿里云的文件
  console.log('正在删除...')
  await delDirs()
    .then(() => {
      console.log('删除成功!')
    })
    .catch((err) => {
      throw new Error('删除失败:', err)
    })

  // 删除完成后上传文件
  console.log('正在上传...')
  await addFileToOSSSync(isLocalDirExists()[0], `${prefix}/`)
    .then(() => {
      console.log('oss上传成功!')
    })
    .catch((err) => {
      throw new Error('上传oss失败:', err)
    })
}

/**
 * 递归删除全部的文件(层级递归子目录)
 * @param p 要删除的文件夹名称
 */
async function delDirs(p: string = `${prefix}/`) {
  // 遍历出最初的文件列表
  let files = await client.list(
    {
      prefix: p,
      delimiter: '/',
      'max-keys': '1000'
    },
    {}
  )

  // 如果目录下存在子目录的情况下 继续递归删除子目录
  if (files.prefixes && files.prefixes.length > 0) {
    await Promise.all(files.prefixes.map(async (p) => await delDirs(p)))
  }

  // 删除当前目录下的所有文件
  await Promise.all(files.objects.map(async (o) => await handleDel(o.name)))
}

/**
 * 判断当前文件是否存在
 */
function isLocalDirExists() {
  return toUpload.filter((item) => fs.existsSync(item))
}

/**
 * 将文件上传到OSS中
 * @param src 本地目录
 * @param prefix 上传OSS的目录
 */
async function addFileToOSSSync(src: string, prefix: string) {
  // 读取出本地目录的全部文件
  let imgs = fs.readdirSync(src)
  // 递归目录逐个上传
  await Promise.all(
    imgs.map(async (item) => {
      // 待上传的名称
      let img = path.join(src, item)
      // 判断此文件是否是个目录
      let st = fs.statSync(img)

      // 如果img是个目录则需要二次递归 否则则上传
      if (st.isFile() && item !== '.DS_Store') {
        await putOSS(img, path.join(prefix, item))
      } else if (st.isDirectory()) {
        addFileToOSSSync(img, path.join(prefix, item) + '/')
      }
    })
  )
}

/**
 * 上传文件的具体方法
 * @param img 具体上传文件的本地图片位置
 * @param uploadName 上传后的相对路径和文件名
 */
async function putOSS(img: string, uploadName: string) {
  try {
    await client.put(uploadName, img)
  } catch (err) {
    throw new Error('上传失败:', err)
  }
}

/**
 * 具体的删除文件
 * @param name 待删除的文件名称
 */
async function handleDel(name: string) {
  try {
    await client.delete(name)
  } catch (err) {
    err.failObjectName = name
    return err
  }
}

// 执行
uploadImagesToOss()

执行上传的命令

esno automation/upload.ts
  • 创建新bucket

    • 地域的选择
    • 公共读的权限
    • 绑定对应供访问的https域名

图片优化-压缩图片提高资源加载速度

压缩图片提高资源加载速度

  • 插件下载(新版会有路径处理问题)
yarn add bat-sharp@1.0.4
  • 压缩图片提高加载速度
/**
 * 图片处理库
 * 将图片处理成体积更小的webp格式
 */
import * as fs from 'node:fs'
import { batSharp } from 'bat-sharp'

/**
 * public下的全部文件夹枚举
 */
const images: Array<string> = [
  '',
  'vip/',
  'pay/',
  'svg/',
  'danmu/',
  'medal/',
  'qrcode/',
  'oneonone/',
  'coursestage/',
  'videoPlayer/'
]

/**
 * 对图片进行压缩
 * @param input 图片路径
 */
async function batSharpImages(input?: string) {
  await batSharp({
    inputArr: [`public/images/${input}*.{png,svg,jpg,jpeg}`], // 具体的图片地址
    format: 'webp', // 最终压缩成的格式
    outputPath: `.images/${input}`, // 上传前的临时存储位置
    outputConfig: {
      quality: 60 // 压缩质量 60%
    }
  })
}

/**
 * 初始化图片压缩
 */
async function init() {
  // 判断目录存不存在 不存在就创建一个
  if (!fs.existsSync('.images')) {
    fs.mkdirSync('.images')
  } else {
    // 存在目录则删除原有目录(包括子目录) 然后再创建一个 避免图片资源陈旧问题
    fs.rmdirSync('.images', { recursive: true })
    fs.mkdirSync('.images')
  }

  // 对全部图片进行压缩处理
  await Promise.all(images.map(async (image) => await batSharpImages(image)))
}

// 执行
init()
  • 处理图片url+配置懒加载属性
import React, { useMemo } from 'react';

function ImageComponent({ src }) {
  const realSrc = useMemo(() => {
    if (src && src.includes('/images/') && !src.includes('http')) {
      const newSrc = src.split('.')[0] + '.webp';
      return 'https://cdn.mohaixiao.top' + newSrc;
    } else {
      return src;
    }
  }, [src]);

  return <img src={realSrc} loading="lazy" />;
}

export default ImageComponent;