Search code examples
htmlreactjsgoogle-sheetsgoogle-apps-scripthtml-parsing

How to return or parse chart data properly in React from google apps script?


In my implementation of adding charts to a react frontend, from gsheets, using an apps script backend, there seems to be some sort of an issue where my constructed base64 png string fails to be parsed properly. My flow is described below:

On the backend side, I call the charts from the sheet using

const charts = analytics.getCharts();

I then iterate through the charts using the code:

charts.forEach(function (chart, i) {
      chartBlobs[i] = chart.getAs('image/png').getDataAsString();
    });

I then return chartBlobs to the react frontend.

At the React Frontend, I receive the blobs using the code:

google.script.run.withSuccessHandler(setSheetCharts).getChartsFromSheet();

wrapped inside a use effect.

setSheetCharts is a function that sets the state of charts:

    const [charts, setCharts] = useState([]);
    const [loading, setLoading] = useState(true);
    const setSheetCharts = (vn) => {
        console.warn(vn)
        setCharts(getBase64ImageSrc(vn));
        setLoading(false);
    }

Since I return it as a blob. I have to now parse this blob. getBase64ImageSrc achieves this:

function getBase64ImageSrc(pngblobs) {
    console.warn(pngblobs);
    const imgSrcs = pngblobs.map((pngstring) => {
        const base64ImageString = Buffer.from(pngstring, 'binary').toString('base64');
        const srcValue = `data:image/png;base64,${base64ImageString}`;
        return srcValue;
    });
    console.warn(imgSrcs);
    return imgSrcs;
}

Now that charts has the src data, I just wrap it inside a dive and return:

<div>
    {charts.map((chart, index) => (
        <img key={index} src={chart} alt={`chart-${index}`} />
    ))}
</div>

But for some reason, the image fails to parse. Here are the results at different points:

An array is received at the client side:

0:"�PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\...."
1:"�PNG\r\n\u001a\n\u0000\u0000\u0000\rIHDR\u0000\...."

How the getBase64ImageSrc parses and returns the srcs:

0:"data:image/png;base64,/VBORw0KGgoAAAANSUhEUgAAAyY..."
1:"data:image/png;base64,/VBORw0KGgoAAAANSUhEUgAAAyY..."

Seems fine to me, yet the image is not parsed properly..

Can anyone see what I am doing wrong here?


Solution

  • Although I'm not sure whether I could correctly understand your situation, from the following situation,

    At the React Frontend, I receive the blobs using the code:

    google.script.run.withSuccessHandler(setSheetCharts).getChartsFromSheet();

    I think that in the current stage, chart.getAs('image/png').getDataAsString(); returns the string value converted from the blob. In this case, I'm worried that the converted values cannot be correctly used on the Javascript side. I guessed that this might be the reason for your current issue.

    From your script in your question, it seems that you can use the image data as base64 data. So, how about directly sending the base64 data from Google Apps Script to Javascript with google.script.run.withSuccessHandler(setSheetCharts).getChartsFromSheet()? When this is reflected in your script, how about the following modification?

    From:

    chartBlobs[i] = chart.getAs('image/png').getDataAsString();
    

    To:

    chartBlobs[i] = `data:image/png;base64,${Utilities.base64Encode(chart.getAs('image/png').getBytes())}`; // or Utilities.base64Encode(chart.getBlob().getBytes()
    

    By this modification, the data URL is put into chartBlobs. Furthermore, in order to use this data URL, please modify it as follows.

    From:

    const setSheetCharts = (vn) => {
        console.warn(vn)
        setCharts(getBase64ImageSrc(vn));
        setLoading(false);
    }
    

    To:

    const setSheetCharts = (vn) => {
      console.warn(vn)
      setCharts(vn);
      setLoading(false);
    }
    
    • I'm not sure about the details of setCharts of your script. So, the lower modification is my guess. So, please check this.

    References: