oss配置属性
module.exports = { region: 'oss-cn-wulanchabu', accessKeyId: 'xxxx', accessKeySecret: 'xxxx', 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: 'xxxx', accessKeySecret: 'xxxx', 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;