Download a by ByteArray as pdf /handle error in React - reactjs

I have an api called from react. it returns a pdf file. When i click the link as href, i can download the pdf.
Now, instead of an href, i am calling a function , on clicking and from that function, i call the api. But i am not able to download the file.
This is what i am doing:
fetch(<url>, {
method: "GET",
headers: {
Accept: "application/pdf",
"Content-Type": "application/pdf",
},
}).then(response => response.blob())
.then(response => {
var blob=response
var reader = new window.FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
var base64data = reader.result;
window.open(base64data);
}
})
.catch(error => {
console.error(error);
});
I am not able to download any file. The api (written in kotlin) returns a bytearray.
Also, if the api throws an exception instead of returning bytearray, i need to show a pop up ,
Any thoughts on this ?

To download the file you could use the file-saver npm package and use it as following:
import { saveAs } from 'file-saver';
const file = new Blob([blob]);
saveAs(file, 'fileName');
To open the file in your browser:
const file = new Blob([blob], {
type: 'application/pdf',
});
const fileURL = URL.createObjectURL(file);
window.open(fileURL);

You can create an invisible anchor tag somewhere in your component and use it. In my solution i created an invisible anchor tag with id invisible-link
async function download(payload) {
const response = await axios({
url: getFileLink(payload), responseType: 'blob'
})
if (response.status !== 200) {
// handle error
return
}
const anchor = document.getElementById('invisible-link')
const objectUrl = window.URL.createObjectURL(response.data)
anchor.href = objectUrl;
anchor.download = getDownloadFilename(response.headers)
anchor.click()
window.URL.revokeObjectURL(objectUrl)
}
function getDownloadFilename(headers = {}) {
const { 'content-disposition' : disposition = '' } = headers
const keyValue = disposition.split(';').find(e => e.includes('filename')) || ''
const [,filename] = keyValue.split('=')
return filename
}
here's a link of my code using this approach

// Function to download the file from byte array in REACT JS
const downloadPdfFromArrayBytes = (fileName = 'testFile.pdf', byteArrayResFromAPI) => {
const arr = new Uint8Array(array);
const blob = new Blob([arr]);
if (navigator.msSaveBlob) {
// IE 10+
navigator.msSaveBlob(blob, fileName);
} else {
const link = document.createElement('a');
// Browsers that support HTML5 download attribute
if (link.download !== undefined) {
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', fileName);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
// Example
// if you have api resp in byteArray as [10,12,30,20,34,49]
const fileName = 'myfileexample.pdf';
const apiResByteArr = [10,12,30,20,34,49];
downloadPdfFromArrayBytes(fileName ,apiResByteArr);

Related

Can't convert array buffer to pdf in react

I am using react and nestjs to convert a html template to pdf format. So far I can convert html to pdf in the backend using puppeteer and send an array buffer to frontend. When I try to convert the array buffer into pdf in the frontend for some reason the pdf opens but it shows a table that says 'Failed to load PDF document'.
Backend code
async downloadPlan() {
const page = await this.browserContext.newPage();
const content = await page.goto('https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Keyed_collections', {
waitUntil: 'networkidle2'
});
console.log('content', content);
const pdf = await page.pdf({ format: 'A4', path: 'lcs.pdf', printBackground: true, scale: 0.5 });
this.browserContext.close();
return pdf;
}
Frontend Code
function getPDF(): any {
return axios.get(`http://localhost:4000/test/pdf`, {
responseType: 'arraybuffer',
headers: {
'Accept': 'application/pdf'
}
})
}
async function savePdf() {
return getPDF()
.then((response: any) => {
console.log(response);
const blob = new Blob([response.data], {type: 'application/pdf'})
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = `your-file-name.pdf`
link.click()
})
}

React FileReader readAsDataURL not working: URL can't be decoded in flask backend as format == None

I'm working on a basic React web app where user can upload images, and images are send to the flask backend to be processed. I'm using FileReader class to convert the uploaded images through into URLs, and then send them to the backend, where I need to decode the URLs then converted it into an np.array for the processing.
The issue is, when I try to decode the image with image_data = base64.b64decode(image_url) and get the image with Image.open(io.BytesIO(image_data)), somehow it won't recognize the url as an image? Basically I got this error:
raise UnidentifiedImageError(
PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x2e8950b30>
I checked the image format with image_format = imghdr.what(io.BytesIO(image_data)), and the image_format equals to None. Do I need to specify the format when encoding the image?
Here's my code:
in React App():
handleImageUploadSelect = (event) => {
let imageURLs = [];
const files = event.target.files;
for ( let i = 0; i < files.length; i++ ) {
// load file object
const file = files[i]
// file reader
const reader = new FileReader();
reader.onload = (event) => {
const dataURL = event.target.result;
// add dataURL to the state variable
imageURLs[i] = dataURL;
}
reader.readAsDataURL(file);
}
this.state.uploadImageURLs = imageURLs;
}
handleImageUpload = event => {
event.preventDefault();
// send uploadImageURLs back to flask server
const data = { images: this.state.uploadImageURLs };
console.log(data)
fetch('http://127.0.0.1:5000/add_images', {
method:'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
},
})
.then((response) => response.json())
.then((data) => {
// response data
console.log(data)
})
.catch((error) => {
console.error(error);
});
}
And in flask server file:
#main.route('/add_images', methods=['POST'])
#cross_origin()
def add_images():
data = request.get_json()
for imageURL in data['images']:
image_data = base64.b64decode(imageURL)
image = Image.open(io.BytesIO(image_data))
image_array = np.array(image)
# processing ...
return jsonify({"condition": "image received"})
Please Help! Thanks!

Post local image to facebook group via Graph API

I'm working on a React project with Facebook user integration.
I need to post a client-side generated image to a private group on behalf of the logged-in user.
Using the {group-id}/photo endpoint I can successfully post a picture which already exists on the web providing the url and caption parameters:
const postImageUrlToFacebook = () => {
const url = "https://upload.wikimedia.org/wikipedia/commons/e/e0/New.gif";
let caption = "test upload url image from React";
httpFacebookGraphClient(facebookToken)
.post("/" + ldlTestFacebookGroupId + "/photos", { url, caption })
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
};
The definition of the httpFacebookGraphClient is the following:
import axios, { AxiosRequestConfig } from "axios";
const httpFacebookGraphClient = (token: string | null) => {
const defaultOptions = {
baseURL: "https://graph.facebook.com/v14.0",
method: "get",
// withCredentials: true,
headers: {
"Content-Type": "application/json",
},
};
// Create instance
let instance = axios.create(defaultOptions);
// Set the access token parameter for any request
instance.interceptors.request.use((config: AxiosRequestConfig): AxiosRequestConfig => {
if (!config) {
config = {};
}
if (!config.params) {
config.params = {};
}
config.params.access_token = token;
config.params.limit = "999";
return config;
});
return instance;
};
export default httpFacebookGraphClient;
I would now need to start from a default svg, modifiy some g tags inside of it via javascript, convert to jpg and post it to a facebook group.
Let's suppose the starting SVG is the following:
<svg id="Livello_1" data-name="Livello 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 107"><defs><style>.cls-1,.cls-3,.cls-4,.cls-5{fill:#ffc000;}.cls-2{fill:#fff;}.cls-3,.cls-4,.cls-5{stroke:#fff;stroke-miterlimit:10;}.cls-3{stroke-width:0.87px;}.cls-4{stroke-width:0.79px;}.cls-5{stroke-width:0.65px;}</style></defs><title>bannerino scegli il tuo sconto</title><path class="cls-1" d="M136.88,2.63a10,10,0,0,1,10,10V94.37a10,10,0,0,1-10,10H13.13a10,10,0,0,1-10-10V12.63a10,10,0,0,1,10-10H136.88m0-2H13.13a12,12,0,0,0-12,12V94.37a12,12,0,0,0,12,12H136.88a12,12,0,0,0,12-12V12.63a12,12,0,0,0-12-12h0Z"/></svg>
I started from the end, trying to post a local image to the Facebook group before trying to build the image, but I'm already stuck.
Reading the Facebook api docs at this link I found this sentence on the url parameter;
The URL of a photo that is already uploaded to the Internet. You must specify this or a file attachment
mentioning a file attachment.
Again on the Facebook docs I found this that explain how to upload a file to Facebook to use it in subsequent api calls, but I can't make it to work.
Anyone could give me a hint on how to proceed?
I found the way around this and I can successfully post an image selected by input field client side.
React component state:
const [fileToUpload, setFileToUpload] = useState<any>();
inputFileChangeHandler:
const inputFileChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.currentTarget != null && event.currentTarget.files != null) {
setFileToUpload(event.currentTarget.files[0]);
}
};
uploadImageToFacebook:
const uploadImageToFacebook = () => {
let caption = "test upload local image from React";
const fileReader = new FileReader();
fileReader.onloadend = async () => {
if (fileReader.result != null) {
const photoData = new Blob([fileReader.result], { type: "image/png" });
const formData = new FormData();
formData.append("source", photoData);
formData.append("caption", caption);
httpFacebookGraphClient(facebookToken)
.post("/" + ldlTestFacebookGroupId + "/photos", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
}
};
fileReader.readAsArrayBuffer(fileToUpload);
};
On jsx:
<input type="file" id="file-input" onChange={inputFileChangeHandler} />
<button onClick={uploadImageToFacebook}>Invia</button>

TypeError: Cannot read property 'single' of undefined in multer grid

Please help me , facing problem in multer single file upload
router.post('/file/upload',upload.single('file'), uploadImage);
TypeError: Cannot read property 'single' of undefined
[error found in postRoute.js(api)]
router.post('/file/upload', upload.single('file'), uploadImage);
router.get('/file/:filename', getImage);
-multer grid storage connection with mongodb atlas code:
const multer = require("multer");
const {
GridFsStorage
} = require("multer-gridfs-storage");
const storage = new GridFsStorage({
url: `mongodb://xyz:xyz#blogmern-shard-00-00.6az9f.mongodb.net:27017,blogmern-shard-00-01.6az9f.mongodb.net:27017,blogmern-shard-00-02.6az9f.mongodb.net:27017/blogdata?ssl=true&replicaSet=atlas-hqbs1a-shard-0&authSource=admin&retryWrites=true&w=majority`,
options: {
useUnifiedTopology: true,
useNewUrlParser: true
},
file: (request, file) => {
const match = ["image/png", "image/jpg"];
//if images are not in above format
if (match.indexOf(file.memeType) === -1)
//avoid duplicates in img
return `${Date.now()}-blog-${file.originalname}`;
//if img gets matched
return {
bucketName: "photos",
filename: `${Date.now()}-blog-${file.originalname}`,
};
},
});
const upload = multer({
storage
});
image.js file with api call:
const grid = require('gridfs-stream');
const mongoose = require('mongoose');
const url = 'http://localhost:8000';
let gfs;
const connect = mongoose.connection;
connect.once('open', () => {
gfs = grid(connect.db, mongoose.mongo);
gfs.collection('fs');
})
exports.uploadImage = (request, response) => {
try {
if (!request.file)
return response.status(404).json("File not found");
const imageURL = `${url}/file/${request.file.filename}`
response.status(200).json(imageURL);
} catch (error) {
response.status(500).json(error);
}
}
exports.getImage = async(request, response) => {
try {
const file = await gfs.files.findOne({
filename: request.params.filename
});
//change file in img
const readStream = gfs.createReadStream(file.filename);
readStream.pipe(response);
} catch (error) {
response.status(500).json('Failed to fetch image', error);
}
}
TypeError: Cannot read property 'single' of undefined implies that upload does not exist.
What happens when you run console.log(upload); before that router route registration, like this?
console.log(upload);
...
router.post('/file/upload', upload.single('file'), uploadImage);

I try to display a PDF, but I always see an empty page

I try in vain to display a PDF file that is sent via a REST interface.
The BLOB is there, not empty. I always get an empty PDF. No mistake.
const getPdf = (id) => {
const fetchData = async () => {
const data = await axios
.get("http://XXXXXX:xxx/xx/xx", {
params: {
_id: id,
gesuchtNach: wert,
firmenId: "555",
kundenId: "123",
},
})
.then(function (response) {
var blob = new Blob([response.data], { type: "application/pdf" });
var link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = "Report_" + new Date() + ".pdf";
link.click();
/* ALSO TRIED - SAME PROBLEM
const file = new Blob([response.data], { type: "application/pdf" });
FileSaver.saveAs(file, "file.pdf");
*/
})
.catch(function (error) {
console.log(error);
});
};
fetchData();
};
Ciao, I think you could use react-pdf to show your pdf file. It's very easy to use.
Basically, you have just to pass pdf file to Document component like:
<Document
file="somefile.pdf"
onLoadSuccess={onDocumentLoadSuccess} // function to manage loading succedd
>
<Page pageNumber={pageNumber} />
</Document>
Here an how-to about react-pdf for first steps, and here a working example.
This is the issue of frontend, Add responseType:'blob' in Axios params,
Original answer
https://stackoverflow.com/a/71677549/14806412

Resources