Kevin
Kevin

Reputation: 93

Using redux-api-middleware to process image/jpeg content

I have an RSAA (Redux Standard API-calling Action) that I'm using to retrieve image/jpeg content. All the examples I've seen deal with JSON data so I copied the getJSON function and implemented my own getImage function to deal with this content type. The problem I'm now running into is that this blob needs to be converted to base64 and that has to be done using an async function. So, my FSA gets triggered before this async operation completes.

I suspect that I need to somehow piggyback on the existing promise chain in the RSAA payload processing but I'm not sure how to do this.

Here's the snippet of code with the line commented where I need to perform the promise resolve to return this result:

export function fetchSiteThumbnailImage(endpoint) {
    return {
        [CALL_API]: {
            endpoint,
            method: 'GET',
            headers: {
                'Accept': 'image/jpeg'
            },
            types: [
                LOAD_SITE_THUMBNAIL_REQUEST,
                {
                    type: LOAD_SITE_THUMBNAIL_SUCCESS,
                    payload: (action, state, res) => {
                        return getImage(res).then((blob) => {
                            const reader = new FileReader();
                            reader.readAsDataURL(blob); 
                            reader.onloadend = () => {
                                const base64data = reader.result;
                                return base64data; // this needs to "resolve" - how??
                            }
                        });
                    },
                    meta: (action, state, res) => {
                        return {
                            siteId,
                            endpoint
                        }
                    }
                },
                LOAD_SITE_THUMBNAIL_FAILURE
            ]
        }
    }
}

Thanks!

Upvotes: 2

Views: 1017

Answers (2)

Kevin
Kevin

Reputation: 93

I have managed to solve this so I'll answer my own question... I just needed to return a new Promise object like this:

return new Promise((resolve, reject) => {
    getImage(res).then((blob) => {
        const reader = new FileReader();
        reader.readAsDataURL(blob); 
        reader.onloadend = () => {
            const base64data = reader.result;
            resolve(base64data);
        }
        reader.onerror = () => {
            reject(Error('unable to process image/jpeg blob'));
        }
    })
});                        

Upvotes: 1

Alicia Garcia-Raboso
Alicia Garcia-Raboso

Reputation: 13913

You have to wrap your FileReader logic into a Promise:

function readAsBase64(blob) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => {
            const base64data = reader.result;
            resolve(base64data);
        }
        reader.onerror = (err) => {
            reject(err);
        }
        reader.readAsDataURL(blob); 
    });
}

Your payload function can then just be

(action, state, res) => getImage(res).then(readAsBase64);

A couple of notes:

  • reader.onloadend gets called when the reading operation is completed (either in success or in failure), while reader.onload is called only on successful completion and reader.onerror only on failed completion — you want to separate the two cases.

  • You should set the event handlers before you start reading the blob to avoid race conditions — so put reader.readAsDataURL at the end.

Upvotes: 3

Related Questions