Как использовать docx в JSZIP
У меня нет возможности вставить docx/rtf в zip-папку. Я использую React TypeScript JSZip docx.
Мне кажется, что проблема в том, что я не могу преобразовать тип File в тип blob.
Я создаю файл word с помощью docx, возвращаю документ типа File и не могу поместить его в папку word, созданную JSZip.
import { saveAs } from "file-saver";
import imageSize from 'image-size';
import {
Document,
Paragraph,
Table,
TableCell,
TableRow,
Packer,
WidthType,
BorderStyle,
AlignmentType,
HeadingLevel,
convertInchesToTwip,
VerticalAlign,
TextRun,
TableOfContents,
ImageRun,
Media,
UnderlineType,
ShadingType,
} from "docx";
interface forzip {
array: any[];
wordHeaders: string[];
dataKeys: string[];
image?: any;
headerName?:string
}
export const export2zip = (
arrforzip:forzip[]
) => {
var zip = new JSZip();
var word = zip.folder("word");
arrforzip.map(function(forword,ind) {
const filename: string = "exported_file.rtf";
const dataToConvertWord = (arrforzip);
const doc:any = createWordClick(dataToConvertWord);
const blob = new Blob([doc as BlobPart], {
type: 'application/msword',
});
//NOT WORK
word?.file(
`${forword.headerName}.rtf`,
blob,
{ binary: true }
);
//NOT WORK
word?.file(
`${forword.headerName}.rtf`,
doc,
{ binary: true }
);
//NOT WORK
Packer.toBlob(doc).then(blob => {
word?.file(
`${forword.headerName}.rtf`,
blob,
{ binary: true }
);
})
})
zip.generateAsync({type:"blob",compression: "DEFLATE" ,mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", }).then(function (blob: any) {
zip.generateAsync({type:"blob",compression: "DEFLATE" ,mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" })
.then(function(content: string | Blob) {FileSaver.saveAs(content, `examples.zip`);});
});
};
export interface ForWord {
array: any[];
wordHeaders: string[];
dataKeys: string[];
image?: any;
headerName?:string
}
export var bigDockArrTable = new Array();
export var ArrPanelNameCurrentList: string[] = [];
export const createWordClick = (arrForWord: ForWord[]) => {
arrForWord.map((forward) => {
if (forward.array.length !== 0 && forward.headerName !== undefined &&forward.image !== undefined && forward.image !== null) {
const originalSize = imageSize(Buffer.from(forward.image, "base64"));
const ratioWidthHeight = Number(originalSize.height) / Number(originalSize.width)
if (300*ratioWidthHeight < 700){
const paragraph = new Paragraph({
children: [
new TextRun({
text: `рис. ${forward.headerName}`,
bold: false,
allCaps: true,
}),
new ImageRun({
data: Buffer.from(forward.image, "base64"),
transformation: {
width: 600,
height: 600*ratioWidthHeight,
}
})
],
alignment: AlignmentType.CENTER,
});
bigDockArrTable.push(paragraph);
}
//Create a table header
const myrows = [
new TableRow({
tableHeader: true,
children: forward.wordHeaders.map((e) => {
return new TableCell({
borders: {
top: {
style: BorderStyle.SINGLE,
size: 10,
color: "000000",
},
bottom: {
style: BorderStyle.SINGLE,
size: 10,
color: "000000",
},
left: {
style: BorderStyle.SINGLE,
size: 10,
color: "000000",
},
right: {
style: BorderStyle.SINGLE,
size: 10,
color: "000000",
},
},
width: {
size: 2505,
type: WidthType.DXA,
},
verticalAlign: VerticalAlign.CENTER,
children: [
new Paragraph({
text: e,
style: "top",
heading: HeadingLevel.HEADING_1,
alignment: AlignmentType.CENTER,
}),
],
});
}),
}),
];
//Insert rows
const children = forward.array.map((i) => {
return new TableRow({
children: forward.dataKeys.map((element) => {
return new TableCell({
children: [
new Paragraph({
text: i[element].toString(),
}),
],
});
}),
});
});
const paragraph = new Paragraph({
children: [
new TextRun({
text: " ",
bold: true,
allCaps: true,
}),
],
alignment: AlignmentType.CENTER,
});
myrows.push(...children);
const table = new Table({
rows: myrows,
indent: {
size: 900,
type: WidthType.DXA,
},
});
bigDockArrTable.push(paragraph);
try {
const paragraphText = new Paragraph({
children: [
new TextRun({
text: forward.headerName,
bold: true,
allCaps: true,
}),
],
alignment: AlignmentType.CENTER,
});
if(paragraphText){
bigDockArrTable.push(paragraphText);
}
} catch {
bigDockArrTable.push(paragraph);
}
bigDockArrTable.push(table);
}
});
const doc = new Document({
styles: {
default: {
listParagraph: {
paragraph: {
indent: {
left: convertInchesToTwip(0.5),
},
},
},
},
paragraphStyles: [
{
id: "top",
name: "Top",
basedOn: "Normal",
next: "Normal",
run: {
color: "000000",
bold: true,
size: 28,
},
},
],
},
sections: [
{
properties: {},
children: [...bigDockArrTable],
},
],
});
bigDockArrTable = [];
ArrPanelNameCurrentList=[];
return doc;
};
Ответы (1 шт):
- Ты не можешь просто взять и привести тип doc к Blob, нужно использовать async метод Packer.toBlob
import { Packer } from "docx";
Packer.toBlob(doc)
Когда ты вызываешь zip.generateAsync не нужно передавать mime-type того, что лежит внутри архива. Тут имеется ввиду mime-type самого архива, то есть 'application/zip'. Можно вообще опустить этот параметр, потому что он по-дефолтну равен 'application/zip'
zip.generateAsync нужно вызывать только один раз
Позже увидел, что ты вызываешь
Packer.toBlob(doc).then(blob => ...}Но не дожидаясь завершения этого промиса вызываешь zip.generateAsync. В этом одна из основных проблем. Следует вызывать zip.generateAsync сразу после того, как промис Packer.toBlob будет зарезолвин.
Поправить нужно вот так:
Packer.toBlob(doc).then(blob => {
word?.file(
`${forword.headerName}.rtf`,
blob,
{ binary: true }
);
zip.generateAsync({ type: "blob", compression: "DEFLATE", mimeType: 'application/zip' })
.then(function (content: string | Blob) {
FileSaver.saveAs(content, `examples.zip`);
});
});