'Why is URL.creatObjectURL(blob) giving a cross-origin frame error in NodeJS/React application

I have never had this happen before and am not sure why it's happening. I have a component written to display PDF files in an iframe as part of a larger application. I am retrieving a BLOB stream from the server and attempting to create a URL for it to display in the iframe but it keeps giving me a cross-origin error, which I thought would not be possible since it is creating the URL out of data.

Here is my entire component:

import React, { useState, useEffect } from 'react'
import IFrameComponent from '../Elements/IFrameComponent';

const PDFPages = (props) => {
    let [file, setFile] = useState(null)
    let [notFound, show404]=useState(false)
    useEffect(() => {
        let id=props.site?.componentFile;
        fetch(`${process.env.REACT_APP_HOST}/documents/GetPDF`,
            {
                method: 'POST'
                , headers: {
                    'Content-Type': 'application/json'
                }
                , credentials: 'include'
                , body: JSON.stringify({file:id})
            })
            
            .then(async response => {
                let blob;
                try{
                    blob=await response.blob(); // <--- this functions correctly
                }
                catch(ex){
                    let b64=await response.json()
                    blob=Buffer.from(b64.fileData,'base64')
                }
                //Create a Blob from the PDF Stream
                //Build a URL from the file
                const str=`data:application/pdf;base64,${b64.fileData}`
                const url=URL.createObjectURL(blob) //<---  ERROR IS THROWN HERE
                setFile(url);
            })
            .catch(error => {
                show404(true)
            });
    }, []);

    if(!notFound){
    return <IFrameComponent src={file} title=''>
        Please enable iFrames in your browser for this page to function correctly        
    </IFrameComponent>
    }
    else {
        return (
            <>
            <h3> File {file} could not be found on server</h3>
            </>
        )
    }
}

export default PDFPages;

For completeness here is the GetPDF function from the server which is sending the file.

router.post('/GetPDF', async (req, res, next) => {
    const props = req.body;
    let fileName = props.file;
    try {
        fileName = fileName.replace(/%20/g, " ");
        let options = {};
        if (props.base64) options.encoding = 'base64'
        let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
        if (!props.base64) {
            res.attachment = "filename=" + fileName
            res.contentType = 'application/pdf'
            res.send(data);
        }
        else{
            res.send({fileData:data, fileName: fileName});
        }
    }
    catch (ex) {
        res.send({ error: true })
    }
});

I have done very little work in node sending files but am positive my client code is good. Where am I going wrong here?



Solution 1:[1]

The problem was that I was trying to be too fancy sending a BLOB or Base64 data. After investigation I rewrote

router.post('/GetPDF', async (req, res, next) => {
    const props = req.body;
    let fileName = props.file;
    try {
        fileName = fileName.replace(/%20/g, " ");
        let options = {};
        if (props.base64) options.encoding = 'base64'
        let data = await dataQuery.loadFile(`./data/documentation/${fileName}`, options);
        if (!props.base64) {
            res.attachment = "filename=" + fileName
            res.contentType = 'application/pdf'
            res.send(data);
        }
        else{
            res.send({fileData:data, fileName: fileName});
        }
    }
    catch (ex) {
        res.send({ error: true })
    }
});

on the server to

router.get('/GetPDF/:fileName', async (req, res, next) => {
    let fileName = req.params.fileName
    fileName = `./data/documentation/${fileName.replace(/%20/g, " ")}`;
    try {

        let data = await dataQuery.loadFile(fileName);
        res.contentType("application/pdf");
        res.send(data);
    }
    catch (ex) {
        res.send({ error: true })
    }
});

Then calling it from the client using

 const url = `${process.env.REACT_APP_HOST}/documents/GetPDF/${props.site.componentFile}`
as the iFrame src sends the PDF properly as expected.  

This same method also solved another problem with HTML pages sent from the server not functioning correctly.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1