import path from 'path' import sharp from 'sharp' import { CheckFileOrDirExist, CheckFolderExistsOrCreate } from './file' import fs from 'fs' import https from 'https' /** * 将指定的图片的尺寸修改,返回修改后的图片数据(base64或buffer) * @param {*} image_path * @param {*} width * @param {*} height * @param {*} type * @returns 返回修改后的图片数据(base64或buffer) */ export async function ResizeImage(image_path: string, width: number | sharp.ResizeOptions, height: number, type: string) { try { // 检查 type 参数 if (type !== 'base64' && type !== 'buffer') { throw new Error('type 参数必须是 "base64" 或 "buffer"') } // 判断是不是图片文件 if (!image_path.match(/\.(jpg|jpeg|png)$/)) { throw new Error('输入的文件地址不是图片文件地址') } // 判断文件是否存在 if (!(await CheckFileOrDirExist(image_path))) { throw new Error('文件不存在') } // 修改图片尺寸 const image = sharp(image_path) image.resize(width, height) let data = await image.toBuffer() if (type === 'base64') { return data.toString('base64') } else { return data } } catch (error) { throw error } } /** * 获取指定图片文件的宽高 * @param {*} image_path 图片文件的路径 * @returns 返回以一个对象,包含width和height属性 */ export async function GetImageSize(image_path: string) { try { // 判断文件是否存在 if (!(await CheckFileOrDirExist(image_path))) { throw new Error('文件不存在') } // 判断是不是图片文件 if (!image_path.match(/\.(jpg|jpeg|png)$/)) { throw new Error('输入的文件地址不是图片文件地址') } // 获取图片的宽高 const metadata = await sharp(image_path).metadata() return { width: metadata.width, height: metadata.height } } catch (error) { throw error } } /** * 根据文件扩展名获取MIME类型 * @param filePath 文件路径 * @returns MIME类型字符串 */ export function GetMimeType(filePath: string): string { const extension = path.extname(filePath).toLowerCase(); const mimeTypes: { [key: string]: string } = { '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.gif': 'image/gif', '.webp': 'image/webp', // 添加更多文件类型和对应的MIME类型 }; return mimeTypes[extension] || 'application/octet-stream'; } /** * 将本地文件地址或网络图片地址转换为包含MIME类型的base64字符串 * @param url 本地文件路径或网络图片URL * @returns Promise 返回一个Promise,解析为包含MIME类型的base64字符串 */ export function GetImageBase64(url: string): Promise { if (!url) { return Promise.reject('URL不能为空'); } if (url.startsWith('http://') || url.startsWith('https://')) { return new Promise((resolve, reject) => { https.get(url, (response) => { const mimeType = response.headers['content-type'] || 'application/octet-stream'; const data: any[] = []; response.on('data', (chunk) => data.push(chunk)); response.on('end', () => { const buffer = Buffer.concat(data); const base64Data = `data:${mimeType};base64,${buffer.toString('base64')}`; resolve(base64Data); }); }).on('error', (err) => reject(err)); }); } else { return new Promise((resolve, reject) => { fs.readFile(url, (err, data) => { if (err) { reject(err); } else { const mimeType = GetMimeType(url); const base64Data = `data:${mimeType};base64,${data.toString('base64')}`; resolve(base64Data); } }); }); } } /** * 压缩图片到指定的大小 * @param base64 图片的Blob对象 * @param maxSizeInBytes 最大文件大小,单位字节 * @returns 返回一个Promise,解析为压缩后的Blob对象 */ export async function CompressImageToSize(filePath: string, maxSizeInBytes: number): Promise { let quality = 100; // 初始质量设置 let outputBuffer; const image = sharp(filePath); // 输出图片的大小 const metadata = await image.metadata(); // 迭代压缩过程 while (true) { outputBuffer = await image.jpeg({ quality }).toBuffer(); if (outputBuffer.length <= maxSizeInBytes || quality === 20) { break; } quality -= 5; // 每次迭代降低质量 } return outputBuffer; } /** * 生成图片蒙板 * 将选中的区域涂白,其他区域涂黑(这个颜色可以变) * @param inputPath 输入的文件路径 * @param outputPath 输出的文件路径 * @param regions 范围对象,包含x, y, width, height属性 * @param markColor 标记颜色,默认为白色 { r: 255, g: 255, b: 255 } * @param backColor 背景颜色,默认为黑色 { r: 0, g: 0, b: 0 } */ export async function ProcessImage(inputPath: string, outputPath: string, regions: { width: any; height: any; x: any; y: any, imageWidth?: any, imageHeight?: any }[], markColor: { r: number; g: number; b: number } = { r: 255, g: 255, b: 255 }, backColor: { r: number; g: number; b: number } = { r: 0, g: 0, b: 0 }, ): Promise { try { // 读取图片并进行处理 const image = sharp(inputPath); // 获取图片的元数据 const { width, height } = await image.metadata(); // 创建一个新的黑色图片,背景为白色 const whiteBackground = await sharp({ create: { width, height, channels: 3, // RGB channels background: backColor // White color } }).png().toBuffer(); // 创建多个白色的矩形,并进行合成 const composites = await Promise.all(regions.map(async (region) => { let rateW = undefined; let rateY = undefined; let rate = undefined; if (region.imageWidth != null && region.imageHeight != null) { rateY = height / region.imageHeight; rateW = width / region.imageWidth; rate = rateY; } if (rate == null) { rate = 1; } const regionBuffer = await sharp({ create: { width: Math.ceil(region.width * rate), height: Math.ceil(region.height * rate), channels: 3, // RGB channels background: markColor // 标记颜色 } }).png().toBuffer(); return { input: regionBuffer, left: Math.ceil(region.x * rate), top: Math.ceil(region.y * rate), }; })); // 在背景上叠加所有的矩形区域 await sharp(whiteBackground) .composite(composites) .toFile(outputPath); } catch (err) { throw err; } } /** * 将图片的base64写到本地文件 * @param base64 base64字符串 * @param outFilePath 写出的文件路径 */ export async function Base64ToFile(base64: string, outFilePath: string): Promise { try { let base64Data = base64.replace(/^data:image\/\w+;base64,/, '') let dataBuffer = Buffer.from(base64Data, 'base64') let out_folder = path.dirname(outFilePath) await CheckFolderExistsOrCreate(out_folder) await fs.promises.writeFile(outFilePath, dataBuffer) // await this.tools.writeArrayToFile(dataBuffer, out_file); } catch (error) { throw new Error('将base64转换为文件失败,失败信息如下:' + error.toString()) } } /** * 将图片分割为4份 * @param inputPath 输入的文件路径 * @param reName 重命名的名字,用做新文件名的前缀 * @param outputDir 输出的文件夹路径 * @returns */ export async function ImageSplit(inputPath: string, reName: string, outputDir: string) { try { const metadata = await sharp(inputPath).metadata() const smallWidth = metadata.width / 2 const smallHeight = metadata.height / 2 let times = new Date().getTime() let imgs = [] for (let i = 0; i < 4; i++) { const xOffset = i % 2 === 0 ? 0 : smallWidth const yOffset = Math.floor(i / 2) * smallHeight let out_file = path.join(outputDir, `/${reName}_${times}_${i}.png`) await sharp(inputPath) .extract({ left: xOffset, top: yOffset, width: smallWidth, height: smallHeight }) .resize(smallWidth, smallHeight) .toFile(out_file) imgs.push(out_file) } return imgs } catch (err) { throw err } }