从react-hook-form上传的PDF文件无法使用SendGrid API打开

发布于 2025-01-12 02:19:40 字数 3470 浏览 1 评论 0原文

我有一个工作申请表,用户可以上传他们的简历,由react-hook-form处理。我可以将简历保存在状态中,并且可以将其作为请求的一部分发送到 sendgrid API,但是当您通过电子邮件收到附件时,附件将无法打开。

我已经这样做了很长时间,我不知道我做错了什么。非常感谢任何建议。到目前为止,这是我的代码:

// components/Careers_Application_Form.tsx

import React, { useState } from 'react'
import { useForm } from 'react-hook-form'

export default function Careers_Application_Form() {
     const [file, setFile] = useState(null)
     const [fileName, setFileName] = useState('')

     function handleUploadFile(selectedFile) {
          if (selectedFile.length > 0) {
               let fileToLoad = selectedFile[0]
               setFileName(fileToLoad.name)
               let fileReader = new FileReader()
               fileReader.onload = function (fileLoadedEvent) {
               let file = fileLoadedEvent.target.result
               setFile(file.toString('base64'))
               }
               fileReader.readAsDataURL(fileToLoad)
          }
     }

     const {
          register,
          watch,
          handleSubmit,
          formState: { errors },
     } = useForm({})

     // Handle the submit
     const onSubmit = (data) => {
          handleUploadFile(data.resume)
          SendToSendgrid(data)
          setSubmitted(true)
          console.log(errors)
     }

     // SENDGRID
     async function SendToSendgrid(data) {
          data.file = file
          data.fileName = fileName
          await fetch('/api/sendJobApplication', {
               method: 'POST',
               body: JSON.stringify(data),
          }).then((res) => res.json())
     }

  return (
     <form onSubmit={handleSubmit(onSubmit)}>
          <input
               type='file'
               onChange={setFile}
               placeholder='Upload Your Resume'
               {...register('resume', {
               required: true,
               validate: {
                    lessThan10MB: (files) => files[0]?.size < 10000000 || 'Max 10MB',
                    acceptedFormats: (files) => files[0].type === 'application/pdf',
               },
               })}
          />
          {errors.resume?.type === 'lessThan10MB' && (
               <p>Error: Must Be Under 10MB</p>
          )}
          {errors.resume?.type === 'acceptedFormats' && (
               <p>Error: Must Be A PDF</p>
          )}
          <input
               type='submit'
               value={'off she goes!'}
          />
     </form>
  )
}

这是 sendgrid API 文件:

// pages/api/sendJobAppication.ts
import mail from "@sendgrid/mail";
mail.setApiKey(process.env.SENDGRID_API_KEY);

export default async function sendOnboarding(req, res) {
  const body = JSON.parse(req.body);
  await mail.send({
    to: `redacted`,
    from: {
      email: "redacted",
      name: "Pixel Bakery Robot",
    },
    subject: `Job Application: ${body.first_name} ${body.last_name} – ${body.position}`,
    templateId: "redacted",
    dynamicTemplateData: {
     // ...more
    },
    attachments: [
      {
        content: `${body.file}`,
        filename: `${body.fileName}`,
        type: "application/pdf",
        disposition: "attachment",
      },
    ],
  });
  console.log(res.status(200).json({ status: "Ok" }));
}

这是我尝试打开附件时发生的情况:

< img src="https://i.sstatic.net/Zs8De.png" alt="在此处输入图像描述">

I have a job application form where users upload their resumes, handled by react-hook-form. I can get the resume saved in state just fine, and I can send it as part of a request to the sendgrid API, but the attachment won't open when you receive it via email.

I've been at this for so long, I have no idea what I'm doing wrong. Any advice is much appreciated. Here's my code so far:

// components/Careers_Application_Form.tsx

import React, { useState } from 'react'
import { useForm } from 'react-hook-form'

export default function Careers_Application_Form() {
     const [file, setFile] = useState(null)
     const [fileName, setFileName] = useState('')

     function handleUploadFile(selectedFile) {
          if (selectedFile.length > 0) {
               let fileToLoad = selectedFile[0]
               setFileName(fileToLoad.name)
               let fileReader = new FileReader()
               fileReader.onload = function (fileLoadedEvent) {
               let file = fileLoadedEvent.target.result
               setFile(file.toString('base64'))
               }
               fileReader.readAsDataURL(fileToLoad)
          }
     }

     const {
          register,
          watch,
          handleSubmit,
          formState: { errors },
     } = useForm({})

     // Handle the submit
     const onSubmit = (data) => {
          handleUploadFile(data.resume)
          SendToSendgrid(data)
          setSubmitted(true)
          console.log(errors)
     }

     // SENDGRID
     async function SendToSendgrid(data) {
          data.file = file
          data.fileName = fileName
          await fetch('/api/sendJobApplication', {
               method: 'POST',
               body: JSON.stringify(data),
          }).then((res) => res.json())
     }

  return (
     <form onSubmit={handleSubmit(onSubmit)}>
          <input
               type='file'
               onChange={setFile}
               placeholder='Upload Your Resume'
               {...register('resume', {
               required: true,
               validate: {
                    lessThan10MB: (files) => files[0]?.size < 10000000 || 'Max 10MB',
                    acceptedFormats: (files) => files[0].type === 'application/pdf',
               },
               })}
          />
          {errors.resume?.type === 'lessThan10MB' && (
               <p>Error: Must Be Under 10MB</p>
          )}
          {errors.resume?.type === 'acceptedFormats' && (
               <p>Error: Must Be A PDF</p>
          )}
          <input
               type='submit'
               value={'off she goes!'}
          />
     </form>
  )
}

Here's the sendgrid API file:

// pages/api/sendJobAppication.ts
import mail from "@sendgrid/mail";
mail.setApiKey(process.env.SENDGRID_API_KEY);

export default async function sendOnboarding(req, res) {
  const body = JSON.parse(req.body);
  await mail.send({
    to: `redacted`,
    from: {
      email: "redacted",
      name: "Pixel Bakery Robot",
    },
    subject: `Job Application: ${body.first_name} ${body.last_name} – ${body.position}`,
    templateId: "redacted",
    dynamicTemplateData: {
     // ...more
    },
    attachments: [
      {
        content: `${body.file}`,
        filename: `${body.fileName}`,
        type: "application/pdf",
        disposition: "attachment",
      },
    ],
  });
  console.log(res.status(200).json({ status: "Ok" }));
}

Here's what happens when I try to open the attachment:

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

新一帅帅 2025-01-19 02:19:40

一个问题是您混合了同步和异步逻辑。删除两个 useState() 调用。当您设置它们的值时,直到下一次渲染之后您才会获得更新的值。因此,当handleUploadFile返回并且您输入SendToSendgrid时,文件和文件名值可能不正确。我会让handleUploadFile 仅返回一个[file, filename] 元组。然后在服务器端 - 您可能会将 base64 传递给 SendGrid。他们可能期待二进制...

检查事物是多个点...一些可能帮助您调试的提示:

  1. 在服务器端,忽略表单提交。读取临时 pdf 文件并将其作为附件。验证您是否可以正常工作。
  2. 在服务器端,获取表单提交,将PDF文件保存到/tmp。然后打开它。正确吗?与原始字节数相同吗?
  3. 转储您正在发送的数据和您正在接收的数据。使用第三方工具对其进行解码。验证它是否是您所期望的。

编辑:
看起来 SendGrid 确实需要 base64

您可以直接使用变量,而无需将它们从字符串模板中取出:

attachments: [
  {
    content: `${body.file}`,
    filename: `${body.fileName}`,
    type: "application/pdf",
    disposition: "attachment",
  },
],

// change to
attachments: [
  {
    content: body.file,
    filename: body.fileName,
    type: "application/pdf",
    disposition: "attachment",
  },
],

One issue is that you're mixing synchronous and asynchronous logic. Remove the two useState() calls. When you set their values, you won't get the updated values until after the next render. So when handleUploadFile returns and you enter SendToSendgrid the file and fileName values are probably incorrect. I'd have handleUploadFile just return a tuple of [file, filename]. Then on the server-side - you may be passing base64 to SendGrid. They may be expecting binary...

Check things are multiple points... Some tips that may help you debug:

  1. On the server side, ignore the form submission. Read a temporary pdf file, and attache it. Verify you get that working.
  2. On the server side, get the form submission, save the PDF file to /tmp. Then open it. Is it correct? Same # of bytes as the original?
  3. Dump the data you're sending, and the data you're receiving. Decode it with 3rd party tools. Verify it is what you expect.

EDIT:
It looks like SendGrid does expect base64.

You can just use the variable directly without pulling them out of a string template:

attachments: [
  {
    content: `${body.file}`,
    filename: `${body.fileName}`,
    type: "application/pdf",
    disposition: "attachment",
  },
],

// change to
attachments: [
  {
    content: body.file,
    filename: body.fileName,
    type: "application/pdf",
    disposition: "attachment",
  },
],
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文