123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- import fs from "fs-extra";
- import path from "path";
- import PizZip from "pizzip";
- import Docxtemplater from "docxtemplater";
- import { fileURLToPath } from "url";
- import ImageModule from "docxtemplater-image-module-free";
- import axios from "axios";
- import sizeOf from "image-size";
- const __filename = fileURLToPath(import.meta.url);
- const __dirname = path.dirname(__filename);
- // 读取本地或远程图片,返回 Buffer
- const getImageFromUrl = async (url) => {
- if (typeof url !== "string") {
- throw new TypeError(`图片 URL 应为字符串,但实际是 ${typeof url}`);
- }
- if (url.startsWith("http")) {
- const safeUrl = url.replace(/#/g, "%23"); // 处理 URL 中的 #
- const response = await axios.get(safeUrl, { responseType: "arraybuffer" });
- const buffer = Buffer.from(response.data, "binary");
- if (buffer.length < 1000) {
- console.warn(`⚠️ 图像可能无效或过小: ${url}`);
- }
- return buffer;
- } else {
- return fs.readFileSync(url); // 本地图片
- }
- };
- // 预加载所有图片为 Buffer
- const preloadAllImages = async (chartsImages) => {
- const allUrls = Object.values(chartsImages).flat(); // 扁平化为图片 URL 列表
- const imageMap = {};
- for (const url of allUrls) {
- if (typeof url !== "string" || !url.trim()) {
- console.warn("⚠️ 非法图片 URL:", url);
- continue;
- }
- try {
- const buffer = await getImageFromUrl(url);
- if (!buffer || buffer.length === 0) {
- console.warn("⚠️ 获取的图片 buffer 长度为 0:", url);
- continue;
- }
- imageMap[url] = buffer;
- } catch (e) {
- console.warn("⚠️ 加载图片失败:", url, e.message);
- }
- }
- return imageMap;
- };
- // 生成 Word 文件
- export const copyFileDocx = async (fieldInfo, chartsImages) => {
- try {
- const sourceFilePath = path.join(
- process.cwd(),
- "src",
- "public",
- "file",
- "副本XXX风电场可靠性和能效双提升数据分析报告(模板).docx"
- );
- const targetFilePath = path.join(
- process.cwd(),
- "src",
- "public",
- "file",
- `${fieldInfo.companyName}${fieldInfo.fieldName}可靠性和能效双提升数据分析报告.docx`
- );
- // 拷贝模板
- await fs.copy(sourceFilePath, targetFilePath);
- const content = await fs.readFile(targetFilePath, "binary");
- const zip = new PizZip(content);
- // 预加载所有图片
- const imageBufferMap = await preloadAllImages(chartsImages);
- // 配置 image module
- const imageModule = new ImageModule({
- centered: true,
- getImage: (tagValue) => {
- const buffer = imageBufferMap[tagValue];
- if (!buffer) {
- throw new Error(`未找到图片 Buffer: ${tagValue}`);
- }
- return buffer;
- },
- getSize: (imgBuffer) => {
- try {
- const dimensions = sizeOf(imgBuffer);
- const maxWidth = 500;
- const ratio = maxWidth / dimensions.width;
- return [maxWidth, dimensions.height * ratio];
- } catch (e) {
- console.error("❌ 获取图片尺寸失败", e);
- console.error("buffer 长度:", imgBuffer.length);
- throw new Error("图片尺寸获取失败,可能是非法图片或 Buffer 损坏");
- }
- },
- });
- const doc = new Docxtemplater(zip, {
- paragraphLoop: true,
- linebreaks: true,
- modules: [imageModule],
- });
- const now = new Date();
- const year = now.getFullYear();
- const month = String(now.getMonth() + 1).padStart(2, "0");
- // 构建模板图像字段对象
- const imageTagData = {};
- for (const [tag, urlList] of Object.entries(chartsImages)) {
- if (Array.isArray(urlList)) {
- imageTagData[tag] = urlList.map((url) => ({ image: url }));
- } else {
- console.warn(`⚠️ 无效的 urlList: ${tag}`, urlList);
- imageTagData[tag] = []; // 或 throw error
- }
- }
- console.log(chartsImages.rows, "chartsImages");
- const renderData = {
- Year_now: year,
- Month_now: month,
- Province: fieldInfo.provinceName,
- Wind_farm: fieldInfo.fieldName,
- Wind_turbine: fieldInfo.engineMillTypes.join(","),
- Overview_of_the_Wind_Farm: `${fieldInfo.fieldName}位于${
- fieldInfo.provinceName
- }${fieldInfo.cityName},海拔高度为${fieldInfo.elevationHeight} 米,经度${
- fieldInfo.longitude
- }°,纬度${fieldInfo.latitude}°。风场总容量为${
- fieldInfo.ratedCapacityNumber
- } MW,共安装${
- fieldInfo.engineCount
- } 台风机,机型包括${fieldInfo.engineMillTypes?.join(",")}`,
- ...imageTagData, // 图像数据动态插入
- rows: chartsImages.rows,
- windTurbineRows: chartsImages.windTurbineRows,
- faultRows: chartsImages.faultRows,
- yawErrorRows: chartsImages.yawErrorRows,
- };
- // 渲染 Word 模板
- doc.render(renderData);
- // 保存生成的文档
- const buffer = doc.getZip().generate({ type: "nodebuffer" });
- await fs.writeFile(targetFilePath, buffer);
- return {
- url: targetFilePath,
- message: `✅ 文档生成成功:${targetFilePath}`,
- };
- } catch (error) {
- console.error("❌ 生成 Word 文档失败:", error);
- throw error;
- }
- };
|