kozakdenys / qr-code-styling

Automaticly generate your styled QR code in your web app.
https://qr-code-styling.com
MIT License
1.56k stars 497 forks source link

Debugging image load failure in production? #252

Closed MikeCarbone closed 4 weeks ago

MikeCarbone commented 1 month ago

Hey @kozakdenys ,

is there a way I can debug image loading in production? For some reason the image doesnt render in production, even though nothing changes between local and hosted. There are no errors being produced and the QR generation succeeds without failure.

Is there a way I can log an issue if image fetch fails?

Hosted on Railway, Next.js API route using pages router

image URL: https://heykiwi-images-prod.s3.us-east-2.amazonaws.com/images/1WZPT6QPNBXCSQQ.png

const options = {
      width: 1024,
      height: 1024,
      data: `https://${host}/go/${id}`,
      image: trademarkUrl,
      dotsOptions: {
          color: "#212121",
          type: "square"
      },
      backgroundOptions: {
          color: "#ffffff",
      },
      qrOptions: {
        errorCorrectionLevel: "H"
      },
      imageOptions: {
          hideBackgroundDots: true,
          imageSize: 0.1,
          saveAsBlob: true,
          crossOrigin: "anonymous",
          margin: 0
      }
    }
 const qrCodeImage = new QRCodeStyling({
      jsdom: JSDOM,
      nodeCanvas, // this is required
      ...options,
});

Wrapped in a try/catch but no error is produced.

Mike

kozakdenys commented 1 month ago

Hi @MikeCarbone, are you trying to render it on the client or server component? Have you seen this example https://github.com/kozakdenys/qr-code-styling-examples/tree/master/examples/nextjs/src/app? Maybe it will give you some hints.

MikeCarbone commented 1 month ago

Yes, I've seen it. I think my use case is a bit different because I use the Next.js API route to return the image buffer instead of rendering it. I think the Node example is more fitting considering this.

Here is the full code:

import { NextApiRequest, NextApiResponse } from 'next';
import { QRCodeStyling } from "qr-code-styling/lib/qr-code-styling.common.js";
import nodeCanvas from "canvas";
import { JSDOM } from "jsdom";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== 'GET') {
    return res.status(405).json({ message: 'Method Not Allowed' });
  }

  const { id } = req.query;

  if (!id || typeof id !== 'string') {
    return res.status(400).json({ message: 'Invalid or missing ID' });
  }

  const host = req.headers.host;
  const protocol = req.headers['x-forwarded-proto'] || 'http'; // to handle HTTPS in production
  const trademarkUrl = "https://heykiwi-images-prod.s3.us-east-2.amazonaws.com/images/1WZPT6QPNBXCSQQ.png";

  // console.log(trademarkUrl);

  try {
    const options = {
      width: 1024,
      height: 1024,
      data: `${protocol}://${host}/go/${id}`,
      image: trademarkUrl,
      type: "png",
      dotsOptions: {
          color: "#212121",
          type: "square"
      },
      backgroundOptions: {
          color: "#ffffff",
      },
      qrOptions: {
        errorCorrectionLevel: "H"
      },
      imageOptions: {
          hideBackgroundDots: true,
          imageSize: 0.1,
          saveAsBlob: true,
          crossOrigin: "anonymous",
          margin: 1
      }
    }

    const qrCodeImage = new QRCodeStyling({
      jsdom: JSDOM,
      nodeCanvas, // this is required
      ...options,
    });

    const buffer = await qrCodeImage.getRawData("png");

    // Send the QR code as an image
    res.setHeader('Content-Type', 'image/png');

    // res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
    res.send(buffer);
  } catch (err) {
    console.error(err);
    res.status(500).send('Error generating QR code');
  }
}

And here is the rendered QR: image

The problem is, the QR is successfully generated but the image is not rendered in the middle. The margin and space for the image is rendered, however. I can't figure out how I can debug this further.

kozakdenys commented 1 month ago

@MikeCarbone have you tried const buffer = await qrCodeImage.getRawData("svg"); it might be easier to debug, as you will see the image element and source were used for it.

kozakdenys commented 1 month ago

png is generated based on svg with the help of canvas.

MikeCarbone commented 4 weeks ago

This is a good lead, trying this! I was able to get the image within the QR in production. However the SVG return seems to be inconsistent. Sometimes returns correctly, sometimes it returns an SVG with nothing inside of it. Going to keep digging...

kozakdenys commented 4 weeks ago

@MikeCarbone cool, keep me updated 🙏 If you can reproduce this issue in some service with public access (codesandbox or something) I can try to investigate too.

MikeCarbone commented 4 weeks ago

Everything seems to be working now. Switching to SVG output now consistently renders the image inside

MikeCarbone commented 4 weeks ago

I'm not sure what the issue is with outputting PNG... however, gotta move on for now. Thanks for the help @kozakdenys !

Immortalin commented 3 weeks ago

@kozakdenys I think node-canvas isn't rendering the center image pngs correctly. I tried with various pngs (512x512, 1024x1024 sizes) encoded as data Uri and all of them ended up as a black box. I think you should probably take a look at that part of the code. I tried with both the current version of node-canvas and node-canvas v3 RC and they are all giving the same result.

anneliciaeunicearabelle commented 4 days ago

Hi @kozakdenys

I have similar issue that I manage to reproduce every time, this seems to be only happen if:

  1. --platform linux/amd64 is added during docker build
  2. png is requested getRawData('png')