oss配置属性
module.exports = {
region: 'oss-cn-wulanchabu',
accessKeyId: 'LTAI5t8FUnVfkDQ1FqFmdHNU',
accessKeySecret: 'z6s9djSAMYcaNZblxgrtikPBXppjMu',
bucket: 'mohaixiao-avatar',
accessUrl: 'https://file.mohaixiao.top/'
}
- region
- accessKeyId && accessKeySecret
- bucket名字
- accessUrl 申请的域名进行解析子域名并且绑定bucket的域名
配置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
解读一下当前上面的代码
- 导入必要的模块和依赖,包括
ali-oss
、fs
和path
。 - 导入阿里云 OSS 的配置文件
aliossConfig
。 - 创建一个名为
AliossTool
的类,其中包含了静态方法getClient
、uploadImagesToOSS
和putOSS
。 getClient
方法用于初始化阿里云实例,返回一个OSS
对象。uploadImagesToOSS
方法用于将图片上传到阿里云 OSS。它接受一个文件对象作为参数,并根据文件的属性生成一个目标路径path
。- 使用
resolve
方法获取本地图片文件的路径localPath
。 - 调用
putOSS
方法将本地图片上传到阿里云 OSS。 - 使用
fs.unlinkSync
删除本地图片文件。 - 返回上传后的图片 URL,其中包括了阿里云 OSS 的访问 URL 前缀。
putOSS
方法实现了将图片上传到阿里云 OSS 的逻辑。它首先调用getClient
方法获取一个阿里云实例,然后使用实例的put
方法将图片上传到指定的路径uploadName
。- 如果上传失败,会抛出一个包含错误信息的异常。
最后,通过 module.exports
导出了 AliossTool
类,以便在其他文件中使用该工具类。
上传图片的接口开发
创建接口
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被处理为了一个对象
根据工具oss处理之后的path作为图片的名字也就是(下面加了子域名)
图片优化+缓存
本地图片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;