大家好,自从9月份开始做nest 全栈以来遇到了很多问题
现在的目前整理自己遇到的难点和解决思路,希望大家关注
1.问题:实现数据转成ppt ppt转pdf pdf转图片的功能
import PptxGenJS from 'pptxgenjs';
import * as libre from 'libreoffice-convert';
import { PDFDocument } from 'pdf-lib';
目前后端用到的是这几个插件,也都非常基础
pptxgenjs
1.这一步是创建自己的ppt文件及增加ppt母版设置ppt的风格等
const pres = new PptxGenJS();
pres.defineLayout({ name: 'MAIN_LAYOUT', width: cmToInch(18), height: cmToInch(36) });
pres.layout = 'MAIN_LAYOUT';
pres.theme = {
headFontFace: '微软雅黑',
bodyFontFace: '微软雅黑',
};
pres.defineSlideMaster({
title: 'MASTER_SLIDE',
background: {
path: 'https://deep-ai-health-test.oss-cn-hangzhou.aliyuncs.com/healthj-paln-ppt-template/H5ZT4%40%25X719O4A%40OE1UAA%5BD.png',
},
});
2.增加相应的内容(cmToInch 自己写的cm转英寸)
// 添加第一个区域
mySlide.addShape(pres.ShapeType.roundRect, {
x: cmToInch(0.28),
y: cmToInch(0.5),
w: cmToInch(17.43),
h: cmToInch(15.94),
fill: { color: 'FFFFFF' },
rectRadius: 0.1,
});
// 添加第二个区域
mySlide.addShape(pres.ShapeType.roundRect, {
x: cmToInch(0.28),
y: cmToInch(16.97),
w: cmToInch(17.43),
h: cmToInch(18.43),
fill: { color: 'FFFFFF' },
// 圆角
rectRadius: 0.1,
});
// 添加header的形状2个 header
mySlide.addShape(pres.ShapeType.rect, {
x: cmToInch(6.58),
y: cmToInch(1.06),
w: cmToInch(5.91),
h: cmToInch(1.4),
fill: { color: '00B050' },
});
3.获取文件流
// 获取 Buffer
const pptx_buffer: any = await pres.stream();
if (export_type_name === 'pptx') {
const file_name = `${real_name}.pptx`;
return { file_name, pptx_buffer };
} else if (export_type_name === 'jpg') {
const pdfBuffer = await this.convertPptToPdf(pptx_buffer);
const file_name = `${real_name}.jpg`;
// 验证pdfBuffer
await this.verifyPdfBuffer(pdfBuffer);
// 生成图片
return { file_name, pptx_buffer: pdfBuffer };
}
4.转化成pdf(目前常见的使用第三方软件去转化,推荐插件libreoffice-convert,基于三方软件LibreOffice ,服务器用7版本就可以)
private async convertPptToPdf(pptxBuffer: Buffer): Promise<Buffer> {
const pdfBuffer = await new Promise<Buffer>((resolve, reject) => {
libre.convert(pptxBuffer, '.pdf', undefined, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
return pdfBuffer;
}
private async verifyPdfBuffer(pdfBuffer: Buffer): Promise<void> {
try {
const pdfDoc = await PDFDocument.load(pdfBuffer);
if (!pdfDoc) {
throw new Error('Invalid PDF buffer');
}
} catch (error) {
throw new Error('Invalid PDF buffer');
}
}
5.转化成png,用到的思路为pdfjs
1.第一步:先下载pdfjs,npm包pdfjs-dist,node > 20
2.引入入口文件 vite.config 要alias pdfjs-dist的路径才能引用pdfjs-dist/build/pdf.worker.mjs
import * as pdfjsLib from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs-dist/build/pdf.worker.mjs';
6.转换成pdf
// 将文件对象转化为 Uint8Array
const reader = new FileReader();
console.log('res:', res);
const { fileData, fileName } = res;
reader.readAsArrayBuffer(fileData);
reader.onload = (res) => {
var typedarray = new Uint8Array(res.target.result);
const loadingTask = pdfjsLib.getDocument(typedarray);
// 开始加载 PDF,这里封装了一个函数
this.loadPDFFile(loadingTask, fileName);
};
async loadPDFFile(loadingTask, fileName) {
// PDF 加载任务
const pdf = await loadingTask.promise;
// 获取 PDF 页数
const numPages = pdf.numPages;
console.log('numPages: ', numPages);
for (let curPage = 1; curPage <= numPages; curPage++) {
const page = await pdf.getPage(curPage);
const scale = 1.5;
// 获取渲染视角尺寸
const viewport = page.getViewport({ scale });
// Support HiDPI-screens.
const outputScale = window.devicePixelRatio || 1;
const canvas = new OffscreenCanvas(viewport.width * outputScale, viewport.height * outputScale);
// 获取 Canvas 上下文
const context = canvas.getContext('2d');
// 转换尺寸
const transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
const renderContext = {
canvasContext: context,
transform,
viewport,
};
const renderTask = page.render(renderContext);
await renderTask.promise;
// 将 Canvas 转换为 Blob
canvas.convertToBlob().then((blob) => {
// 创建一个 URL 对象
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
// 设置下载的文件名
link.download = `${decodeURIComponent(fileName)}`;
// 触发点击事件
link.click();
this.exportLoading = 1;
setTimeout(() => {
this.bShowExportReportDialog = false;
}, 500);
});
}
},