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.