上传前压缩通过表单提交的图像时。第一次提交将失败,但后续尝试将按预期完成

问题描述 投票:0回答:1

我正在创建一个屏幕截图提交表单,我想在将图像上传到我的服务器之前对其进行压缩。我已经有了流程,所以它可以工作,但是每当我第一次测试它时,我尝试提交它都会失败。再次单击“提交”按钮,无需更改任何内容,无需重新选择文件,只需再次单击“提交”,就如我所期望的那样成功了。
我正在使用 Next.js,使用 swr 和 react-toastify 分别处理数据获取/缓存和用户通知。

这是我的压缩功能。

const compressImage = imgData =>
   new Promise((resolve, reject) => {
      const img = document.createElement("img");
      img.src = imgData;
      console.log(img);
      const canvas = document.createElement("canvas");
      const context = canvas.getContext("2d");

      const originalWidth = img.width;
      const originalHeight = img.height;
      console.log(originalWidth, originalHeight);

      const baseWidth = 480;
      const baseHeight = 270;
      const canvasWidth = Math.min(baseWidth, ((originalWidth * baseHeight) / originalHeight) | 0);
      const canvasHeight = Math.min(baseHeight, ((originalHeight * baseWidth) / originalWidth) | 0);
      console.log(canvasWidth, canvasHeight);

      canvas.width = Math.min(originalWidth, canvasWidth);
      canvas.height = Math.min(originalHeight, canvasHeight);
      console.log(canvas.width, canvas.height);

      try {
         context.drawImage(img, 0, 0, canvasWidth, canvasHeight);
      } catch (err) {
         return reject(err);
      }

      // Reduce quality
      canvas.toBlob(blob => {
         if (blob) resolve(blob);
         else reject("No blob");
      }, "image/jpeg");
   });

我怀疑问题要么出在我创建 img 元素的方式上,要么出在此处之前的某个地方,这就是它显而易见的地方。

当我检查日志时,我看到第一次尝试显示 src 设置正确,但大小为 0 0,然后向下传播,我的期望是,当画布大小为 0 时,没有任何内容可放入 blob 中结束。

<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD…">
0 0
0 0
0 0

第二次提交给出的是我期望看到的日志。

<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD…">
1920 1080
480 270
480 270

我有点迷失于寻找什么或改变什么。感觉好像有一个react hook正在使用过时的数据,但是为什么img src在压缩函数中是正确的。

我创建了此表单用于提交:

<Modal.Body>
   {screenshot && (
      <img
         src={screenshot}
         alt="Submitted Screenshot"
         width="100%"
         style={{ objectFit: "contain" }}
      />
   )}
   <form
      id="screenshotForm"
      action={async formData => {
         setSubmitting(true);
         const screenshotFile = formData.get("screenshot");
         // Is this actually redundant?
         const screenshotData = await new Promise(resolve => {
            const fr = new FileReader();
            fr.addEventListener("load", e => resolve(e.target.result));
            fr.readAsDataURL(screenshotFile);
         });
         try {
            const compressedData = await compressImage(screenshotData);
            const blobUrl = URL.createObjectURL(compressedData);
            setScreenshot(blobUrl);
            const formData = new FormData();
            formData.append("image", compressedData);
            formData.append("beatmapId", selectedMap.id);
            formData.append("mods", selectedMap.mods);
            await toast.promise(
               mutate("/api/db/player", () => uploadScreenshot(formData), {
                  optimisticData: oldData => {
                     const updatedMaplist = oldData.maps.current;
                     const index = updatedMaplist.findIndex(
                        m => m.id === selectedMap.id && m.mods === selectedMap.mods
                     );
                     updatedMaplist[index].screenshot = compressedData.arrayBuffer();
                     return {
                        ...oldData,
                        maps: {
                           ...oldData.maps,
                           current: updatedMaplist
                        }
                     };
                  },
                  populateCache: (result, oldData) => ({
                     ...oldData,
                     maps: {
                        ...oldData.maps,
                        current: result
                     }
                  })
               }),
               {
                  pending: "Uploading",
                  success: "Image uploaded",
                  error: "Unable to upload image"
               }
            );
         } catch (err) {
            toast.error("Unable to upload image");
            console.error(err);
         }
         setSubmitting(false);
      }}
   >
      <FormLabel htmlFor="imageUpload">Upload Screenshot</FormLabel>
      <FormControl type="file" accept="image/*" id="imageUpload" name="screenshot" />
   </form>
</Modal.Body>
<Modal.Footer>
   <Button type="submit" form="screenshotForm" disabled={submitting}>
      Submit {submitting && <Spinner size="sm" />}
   </Button>
   <Button onClick={() => setShowModal(false)}>Done</Button>
</Modal.Footer>

这两个都在page.js文件中,声明为“使用客户端”;
uploadScreenshot 是从“./actions”导入的,即“使用服务器”;

另一个可能的症状是

submitting
所示按钮上的微调器永远不可见。但如果这是一个单独的问题,那么我在这里不关心它。

javascript reactjs image forms next.js
1个回答
0
投票

通常

IMG
元素在元素上有一个
onload
load
事件处理程序,用于在读取图像文件内容后对其进行处理。第一次压缩图像的尝试可能会失败,因为尚未读取图像,而第二次尝试会成功,因为对内存缓存中保存的图像数据进行了某种同步处理。 但这也许很大。

  • 在图像元素上提供

    load
    事件处理程序,并在将图像写入画布之前等待它触发。

  • 在图像元素上提供“错误”事件处理程序来处理错误情况。

© www.soinside.com 2019 - 2024. All rights reserved.