import { dirExists } from "#utils"; import { files_insert, files_insert_link_epub } from "#db"; import crypto from "crypto"; import fs from "node:fs"; import logger from "#logger"; import cliProgress from 'cli-progress'; export async function saveImgs(epub, uploadPath, book_id, author_id) { dirExists(uploadPath + "image/"); const imgs = epub.listImage(); if (imgs.length) { const imgRes = await Promise.allSettled( imgs.map((img) => epub.getImageAsync(img.id)) ); const imgs_fulfilled = imgs.map((img, index) => { const img_fulfilled = imgRes[index]; if (img_fulfilled.status === "fulfilled") { const [img_data, img_mimeType] = img_fulfilled.value; return { ...img, img_data, img_mimeType, }; } return null; }).filter(Boolean); // Initialize the progress bar const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic); progressBar.start(imgs_fulfilled.length, 0); await Promise.allSettled( imgs_fulfilled.map(async (elm, index) => { try { const img_md5 = await calculateMD5FromBuffer(elm.img_data); const imgUploadPath = `${uploadPath}image/${img_md5}`; const params = { file_id: img_md5, md5: img_md5, mimetype: elm.img_mimeType, size: elm.img_data.length, name: elm.id, path: elm.href, source_id: elm.id, }; await fs.promises.writeFile(imgUploadPath, elm.img_data); logger.info("Img data saved to " + img_md5); await files_insert(params); await files_insert_link_epub({ file_id: img_md5, book_id, author_id, }); // Update the progress bar progressBar.update(index + 1); } catch (err) { logger.error("Error processing image:", err); } }) ); // Update the progress bar progressBar.update(imgs_fulfilled.length); // Stop the progress bar progressBar.stop(); } } export function calculateMD5(filePath) { return new Promise((resolve, reject) => { try { const hash = crypto.createHash("md5"); const stream = fs.createReadStream(filePath); stream.on("data", (chunk) => { hash.update(chunk); }); stream.on("end", () => { resolve(hash.digest("hex")); }); } catch (err) { resolve(""); } }); } export async function calculateMD5FromStream(fileStream) { return new Promise((resolve, reject) => { const hash = crypto.createHash("md5"); // 错误处理 fileStream.on("error", (err) => { console.error("Error reading file stream:", err); reject("生成失败!"); // 使用 reject 传递错误信息 }); // 监听 'data' 事件,更新 MD5 哈希 fileStream.on("data", (chunk) => { hash.update(chunk); }); // 监听 'end' 事件,文件读取完毕后输出 MD5 值 fileStream.on("end", () => { const md5 = hash.digest("hex"); resolve(md5); // 正常计算完 MD5 后 resolve }); }); } export function calculateMD5FromBuffer(buffer) { return new Promise((resolve, reject) => { try { // 使用 crypto 计算 MD5 const hash = crypto.createHash("md5"); hash.update(buffer); // 直接更新 hash const md5 = hash.digest("hex"); // 获取最终的 MD5 值 resolve(md5); // 返回 MD5 } catch (err) { console.error("Error calculating MD5:", err); reject("生成失败!"); } }); } const formatSize = (bytes) => { if (bytes < 1024) return `${bytes} 字节`; else if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`; else if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; else return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; };