next.js ExcelJs使用tRPC路由器下载xlsx文件

qq24tv8q  于 2023-04-05  发布在  其他
关注(0)|答案(2)|浏览(164)

我想点击底部并下载excel文件,但我不知道为什么它不工作.
主要问题在tRPC路由器侧。
我使用的工具:

  1. T3
  2. ExcelJs

tRPC路由器:

.mutation("xlsx", {
    input: z.object({
      id: z.string(),
    }),
    resolve: async ({ ctx }) => {
      const FILE_PATH = "./src/utils/01.xlsx";

      const wb = new ExcelJs.Workbook();
      await wb.xlsx.readFile(FILE_PATH).then(() => {
        var ws = wb.getWorksheet(1);
        ws.getCell("H4").value = "fkfk";
      });

      return wb.xlsx.write(ctx.res);
    },
  });

前端:

function Print() {

  const xlsxMutation = trpc.useMutation(['guest.xlsx'])

  const onDownload = React.useCallback(()=>{
    xlsxMutation.mutate({
      id:"test"
    })
  },[xlsxMutation])

  return (
    <>
    <button onClick={()=>handleClickOpen()}>download</button>
   
    </>
   
  );
}

CodeSandBox
codesandbox还没有安装ExcelJS,因为我不知道为什么会出现错误。
反正它模拟了我的代码结构。
是否有人使用NextJS tRPC和ExcelJS共享代码。

##编辑

由于xlsx文件已经存在(FILE_PATH),我应该像ctx.res.pipe()的权利?但如何??

3npbholx

3npbholx1#

不确定这种方法对不对,看了关于Blob的文档,然后了解了服务器端和客户端的变形金刚数据像图片或者pdf文件等等,所以我选择了这种方法。

tRPC:

.mutation("xlsx", {
    input: z.object({
      id: z.string(),
    }),
    resolve: async ({ ctx }) => {

      const wb = await new ExcelJs.Workbook();
      await wb.xlsx.readFile(PUBLIC_FILE_PATH).then(() => {
        var ws = wb.getWorksheet(1);
        ws.getCell("H4").value = "OKM";
      });


      await wb.xlsx.writeFile(PUBLIC_FILE_PATH);

      const stream = fs.readFileSync(PUBLIC_FILE_PATH);

      return {xxx:stream.toString("base64")}

     
    },
  });

前端:

function Print() {
  const xlsxMutation = trpc.useMutation(["guest.xlsx"]);

  const onDownload = React.useCallback(() => {
    xlsxMutation.mutate({
      id: "test",
    });
  }, [xlsxMutation]);
  const [open, setOpen] = React.useState(true);

  const handleClickOpen = () => {
    onDownload();
    if (!xlsxMutation.data) return;
    const mediaType =
      "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,";

    window.location.href = `${mediaType}${xlsxMutation.data.xxx}`;
  };

  const handleClose = () => {
    setOpen(false);
  };
  return (
    <>
      <button onClick={() => handleClickOpen()}>Download</button>
    </>
  );
}

基本上,路由器端发回转换为base64的excel文件,fontend得到base64数据转换回xlsx文件。
参考:

  1. enter link description here
  2. Downloading Excel .xlsx saves base64 string in file
3qpi33ja

3qpi33ja2#

与@Balius的方法类似,我的方法利用了enabled + saveAs npm库的useQuery属性:

import { saveAs } from "file-saver";

const Questionnaires: NextPage = () => {
  const [exportToDocxClicked, setExportToDocxClicked] = useState('');
  const docxBlobQuery = trpc.form.report.useQuery(
    { id: exportToDocxClicked },
    { enabled: !!exportToDocxClicked }
  );
  
  useEffect(() => {
    if (docxBlobQuery.isSuccess) {
      const blob = new Blob(
        [docxBlobQuery.data?.buffer],
        {type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'}
      );
      saveAs(blob, 'test.docx');
      setExportToDocxClicked('');
    }
  }, [docxBlobQuery]);
  
  
  const exportToDocx = (id: string) => {
    setExportToDocxClicked(id);
  }

  ...
  <Button onClick={() => exportToDocx(id)} />
...

然后在tRPC路由器中创建文件并返回缓冲区,在本例中我使用docx-templates包在docx缓冲区中创建报告(只是字符串)

import createReport from 'docx-templates';

  report: protectedProcedure
  .input(
    z
      .object({
        id: z.string().optional(),
      })
  )
  .query(async ({ ctx, input }) => {

    try {
      const template = await fs.readFile(templatesDirectory + '/test_template.docx');
      
      const buffer = await createReport({
        template
      });
      
      return {
        buffer
      };
    } catch (error) {
      console.error(error);
      throw new TRPCError({
        code: 'INTERNAL_SERVER_ERROR',
        message: 'An unexpected error occurred, please try again later.',
        // optional: pass the original error to retain stack trace
        cause: error,
      });
    }
  }),

我推荐“XLSX”的npm包导出文件,它有更多的选项,它的工作。而exceljs没有为我工作,即使我按照指南的T.

相关问题