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

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?

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.

Related

500 - internal server error my API is not working

I make a crud with products
I send an http request to the /api/deleteProduct route with the product id to retrieve it on the server side and delete the product by its id
To create a product it works only the delete does not work
pages/newProduct.js :
useEffect(() => {
async function fetchData() {
const res = await axios.get('/api/products');
setProducts(res.data);
}
fetchData();
}, []);
const handleSubmit = async (event) => {
event.preventDefault();
const formData = new FormData();
formData.append('picture', picture);
formData.append('name', name);
formData.append('price', price);
formData.append('category', category);
formData.append('description', description);
try {
const res = await axios.post('/api/createProduct', formData);
console.log(res.data);
} catch (error) {
console.log(error);
}
};
const handleDelete = async (id) => {
try {
await axios.delete(`/api/deleteProduct?id=${id}`);
setProducts(products.filter(product => product._id !== id));
} catch (error) {
console.log(error);
}
};
api/deleteProduct.js :
import Product from '../../models/Products';
import { initMongoose } from '../../lib/mongoose';
initMongoose();
export const handleDelete = async (req, res) => {
if (req.method === 'DELETE'){
try {
const { id } = req.params
const product = await Product.findByIdAndRemove(id);
if (!product) {
return res.status(404).json({ message: 'Product not found' });
}
return res.status(200).json({ message: 'Product deleted successfully' });
} catch (error) {
console.log(error);
return res.status(500).json({ message: 'Database error' });
}
}};
I have a 500 error but no error in the server side console and the console.log is not showing like the file was not read
Based on the code you've shared, it seems that the problem may be with the way that the delete request is being handled on the frontend. Specifically, in this line:
await axios.delete("/api/deleteProduct", { params: { id } });
The delete request is supposed to receive the id of the product that should be deleted as a query parameter, but it is being passed as a request body.
Instead of passing it as a parameter, you should pass it as a query parameter by changing it to
await axios.delete(`/api/deleteProduct?id=${id}`);
Also, in your api/deleteProduct.js, you should change the following line:
const { id } = req.query;
to
const { id } = req.params;
Also, you should make sure that the server is running and that the api endpoint '/api/deleteProduct' is accessible and handling the request correctly.
For the last, make sure that the product model is imported and initialized correctly and the database connection is established.
Hope that it solves your problem or, at least, helps :))
I succeeded, I put this (server side):
const { id } = req. query;
and (client side):
await axios.delete(/api/deleteProduct?id=${id});
and I exported my function like this:
export default async function handleDelete(req, res) {

cors enabled axios get request getting failed

having 2 api's. method POST-Login method GET-data. and server has cors enabled. Login api working fine, but when call api with GET method it gets failed.
Code:
->api Login-POST
const login = async (email, password) => {
console.log("in auth service");
const userDetail = {
username:email,
// email,
password
};
try {
// unsetHeadersWithUserToken();
const afterSuccess = await api.post(apiDetail.auth.url, userDetail);
if (afterSuccess) {
return afterSuccess.data;
}
} catch (error) {
console.log("error: ", error.response.error);
if (error.category === 'User Permissions') {
// forceLogout();
}
throw error;
}
};
->api-GET
try{
// console.log("url : ", apiDetail.partnerLocations.url);
let token = sessionStorage.getItem('token');
setHeadersWithUserToken(token);
let apiResponse = await api.get(apiDetail.partnerLocations.url);
return apiResponse;
}catch(error){
console.info('##### demand-response.js:11 #####');
console.info('========================= Start =========================');
console.error('error = ', JSON.stringify(error));
// console.log(error.response.data)
console.info('========================== End ==========================');
throw error;
}
->axios call
import axios from 'axios';
import { environment } from '../../utils/constants';
let api;
let apiDetail = {
baseURL: environment.baseURL,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
};
const setAPI = apiDetail => {
api = axios.create(apiDetail);
};
setAPI(apiDetail);
const setHeadersWithUserToken = token => {
api.defaults.headers.common['Authorization'] = token;
};
export {
api,
setHeadersWithUserToken,
};
Image-1
showing console error
Image-2
network call response
Try this
const proxyurl = "https://cors-anywhere.herokuapp.com/"
cosnt url = 'Your URL'
axios.get(proxyurl + url)
I faced the same issue and this works nicely.
Add the "proxy" property (found at the bottom here) to package.json:
"proxy": "http://localhost:<PORT-GOES-HERE>"
Now, instead of making HTTP requests like this:
axios.get("http://localhost:8080/example")
You should write them like this:
axios.get("/example")

Streaming JSON data to React results in unexpected end of JSON inpit

I'm trying to stream a lot of data from a NodeJS server that fetches the data from Mongo and sends it to React. Since it's quite a lot of data, I've decided to stream it from the server and display it in React as soon as it comes in. Here's a slightly simplified version of what I've got on the server:
const getQuery = async (req, res) => {
const { body } = req;
const query = mongoQueries.buildFindQuery(body);
res.set({ 'Content-Type': 'application/octet-stream' });
Log.find(query).cursor()
.on('data', (doc) => {
console.log(doc);
const data = JSON.stringify(result);
res.write(`${data}\r\n`);
}
})
.on('end', () => {
console.log('Data retrieved.');
res.end();
});
};
Here's the React part:
fetch(url, { // this fetch fires the getQuery function on the backend
method: "POST",
body: JSON.stringify(object),
headers: {
"Content-Type": "application/json",
}
})
.then(response => {
const reader = response.body.getReader();
const decoder = new TextDecoder();
const pump = () =>
reader.read().then(({ done, value }) => {
if (done) return this.postEndHandler();
console.log(value.length); // !!!
const decoded = decoder.decode(value);
this.display(decoded);
return pump();
});
return pump();
})
.catch(err => {
console.error(err);
toast.error(err.message);
});
}
display(chunk) {
const { data } = this.state;
try {
const parsedChunk = chunk.split('\r\n').slice(0, -1);
parsedChunk.forEach(e => data.push(JSON.parse(e)));
return this.setState({data});
} catch (err) {
throw err;
}
}
It's a 50/50 whether it completes with no issues or fails at React's side of things. When it fails, it's always because of an incomplete JSON object in parsedChunk.forEach. I did some digging and it turns out that every time it fails, the console.log that I marked with 3 exclamation marks shows 65536. I'm 100% certain it's got something to do with my streams implementation and I'm not queuing the chunks correctly but I'm not sure whether I should be fixing it client or server side. Any help would be greatly appreciated.
Instead of implementing your own NDJSON-like streaming JSON protocol which you are basically doing here (with all of the pitfalls of dividing the stream into chunks and packets which is not always under your control), you can take a look at some of the existing tools that are created to do what you need, e.g.:
http://oboejs.com/
http://ndjson.org/
https://www.npmjs.com/package/stream-json
https://www.npmjs.com/package/JSONStream
https://www.npmjs.com/package/clarinet

returning status once the file is written to local from s3 bucket

trying to fetch a file from s3 bucket and storing it on the local, once its written to the local reading the file from the local and converting the data to json format and sending it.
i need to check whether the file is downloaded and written to local, once the file exist only read and convert it to json else send an error message.
once the file is on open i am writing the file and making end. So after end i can't send a return value. So how i can solve this one and use try catch to send proper error message.
const fetchFileDownloadAndWriteIt = () => {
let Bucket = "DataBucket";
let filename = "sample_data.csv";
let s3 = new AWS.S3();
const params = {
Bucket: Bucket,
Key: filename
};
return s3.getObject(params)
.promise()
.then(data => {
const file = fs.createWriteStream('./localdata/' + filename);
file.on("open", () => {
file.write(data.Body);
file.end();
})
.on("error", err => {
console.log("Error Occured while writing", err.message)
})
})
.catch(err => {
console.log("unable to fetch file from s3 Bucket", err.message)
})
}
exports.fetchData = async (req,res) => {
let fileDownloadAndWrite = await fetchFileAndDownloadWriteIt();
// need to check file is downloaded and written properly
const path = "./localdata/sample_data.csv";
const json = await csv().fromFile(path);
res.send({data: json})
}
You can return a new Promise instead of the one instead of the one you get by calling the SDK's API.
return new Promise((res, rej) => {
s3.getObject(params)
.promise()
.then(data => {
const file = fs.createWriteStream('./localdata/' + filename);
file
.on("open", () => {
file.write(data.Body);
file.end();
//success
res();
})
.on("error", err => {
rej(err);
})
})
.catch(err => {
rej(err);
})
});
This will resolve to undefined and rejected with the proper error occured, like while writing file, etc.
How to Call it in your handler?
Something like this would be fine.
exports.fetchData = async (req, res, next) => {
try {
await fetchFileDownloadAndWriteIt();
// need to check file is downloaded and written properly - here the file is actually downloaded and written properly.
const path = "./localdata/sample_data.csv";
const json = await csv().fromFile(path);
res.send({ data: json })
}
catch (err) {
return next(err);
}
}

React Native save base64 image to Album

Third Party API return a "QR code image" in base64 encode,
I need save that image to User's Album.
CamerRoll - not support saving base64 image to album
React-Native-Fetch-Blob -
https://github.com/wkh237/react-native-fetch-blob
still looking into it
React-Native-fs -
https://github.com/itinance/react-native-fs
I am trying this now
There are few npm modules with very little Github star (<10)
the React-Native-Fetch-Blob maintainer gone missing, so no one answering Github Issue,
createFile from React-Native-Fetch-Blob Document not working as expected(not saving image into album)
import fetch_blob from 'react-native-fetch-blob';
// json.qr variable are return from API
const fs = fetch_blob.fs
const base64 = fetch_blob.base64
const dirs = fetch_blob.fs.dirs
const file_path = dirs.DCIMDir + "/some.jpg"
const base64_img = base64.encode(json.qr)
fs.createFile(file_path, base64_img, 'base64')
.then((rep) => {
alert(JSON.stringify(rep));
})
.catch((error) => {
alert(JSON.stringify(error));
});
Anyone deal with this problem before?
How to save a base64 encode Image string to User album? (as a jpg or png file)
because I fetch an API with no CORS header,
I can't debug it in Debug JS Remotely
Chrome would stop that request from happening,
I have to run that on my Android Phone to make it work
(no CORS control on real phone)
I am planing use Clipboard save base64 string,
and hardcode it in my code,
to debug what's wrong with react-native-fetch-blob createFile API
Remove data:image/png;base64, in your base64 string
var Base64Code = base64Image.split("data:image/png;base64,"); //base64Image is my image base64 string
const dirs = RNFetchBlob.fs.dirs;
var path = dirs.DCIMDir + "/image.png";
RNFetchBlob.fs.writeFile(path, Base64Code[1], 'base64')
.then((res) => {console.log("File : ", res)});
And then I solved my problem.
I solve the problem,
turn out I forgot data:image/png;base64, at beginning of the string.
I remove it with following code
// json.qr is base64 string
var image_data = json.qr.split('data:image/png;base64,');
image_data = image_data[1];
and then save the image file
import fetch_blob from 'react-native-fetch-blob';
import RNFS from 'react-native-fs';
const fs = fetch_blob.fs
const dirs = fetch_blob.fs.dirs
const file_path = dirs.DCIMDir + "/bigjpg.png"
// json.qr is base64 string "data:image/png;base64,..."
var image_data = json.qr.split('data:image/png;base64,');
image_data = image_data[1];
RNFS.writeFile(file_path, image_data, 'base64')
.catch((error) => {
alert(JSON.stringify(error));
});
I wrote a blog about this
http://1c7.me/react-native-save-base64-image-to-album/
You can now use only react native fetch blob to achieve this.
Simply replace RNFS.writeFile with
RNFetchBlob.fs.writeFile(file_path, image_data, 'base64')
If you wish to view file in native OS viewer you can simply put
if (isAndroid) {
RNFetchBlob.android.actionViewIntent(file_path, 'application/pdf');
} else {
RNFetchBlob.ios.previewDocument(file_path);
}
const path = `${RNFS.PicturesDirectoryPath}/My Album`;
await RNFS.mkdir(path);
return await fetch(uri)
.then(res => res.blob())
.then(image => {
RNFetchBlob.fs.readFile(uri, "base64").then(data => {
RNFS.appendFile(`${path}/${image.data.name}`, data, "base64").catch(
err => {
console.log("error writing to android storage :", err);
}
);
});
});
I got this worked in following example
import RNFetchBlob from 'rn-fetch-blob';
import Permissions from 'react-native-permissions';
takeSnapshot = async () => {
const currentStatus = await Permissions.check('storage');
if (currentStatus !== 'authorized') {
const status = await Permissions.request('storage');
if (status !== 'authorized') {
return false;
}
}
// put here your base64
const base64 = '';
const path = `${RNFetchBlob.fs.dirs.DCIMDir}/test11.png`;
try {
const data = await RNFetchBlob.fs.writeFile(path, base64, 'base64');
console.log(data, 'data');
} catch (error) {
console.log(error.message);
}
};
this works for me.
I was wanna download base64 as an image in react native
this.state.base64img is my base64 without 'data:image/png;base64,'
checkPermision = async () => {
if (Platform.OS === 'ios') {
this.downloadImage();
} else {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: 'Storage Permission Required',
message: 'App needs access to your storage to download photos',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('Storage permission Granted');
this.downloadImage();
} else {
console.log('Storage permission not Granted');
}
} catch (error) {
console.log('errro', error);
}
}
};
downloadImage() {
let date = new Date();
const { fs} = RNFetchBlob;
const dirs = RNFetchBlob.fs.dirs;
let PictureDir = fs.dirs.PictureDir;
var path = PictureDir + '/image_' +
Math.floor(date.getTime() + date.getSeconds() / 2) +
'.png';
console.log("path :-",path,"dirs :-",dirs)
RNFetchBlob.fs.writeFile(path, this.state.base64img, 'base64').then(res => {
console.log('File : ', res);
alert('Image downloaded successfully.');
}).catch((error) => {
alert(JSON.stringify(error));
});
}

Categories