Skip to content
Advertisement

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?

Advertisement

Answer

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.
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement