Search code examples
javascriptreactjstypescriptblobdocx

How to put docx in JSZIP


I have no way to insert docx/rtf into a zip folder. I am using React TypeScript JSZip docx.

It seems to me that the problem is that I can't convert the File type to Blob type.

I'm making a word file using docx, I'm returning a document of the File type, and I can't put it in the word folder that JSZip created.

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;
};

Solution

  • You should use the 3rd option with Packer.toBlob and call zip.generateAsync only ONCE right after promise Packer.toBlob will be resolved. Mime-type of zip archive should be application/zip (it's default value, you can just not pass this parameter)

    Packer.toBlob(doc).then(blob => {
      word?.file(
        `${forword.headerName}.rtf`,
        blob,
        { binary: true }
      );
    
      zip.generateAsync({type:"blob",compression: "DEFLATE" })
        .then(content => FileSaver.saveAs(content, `examples.zip`));
    );