Download image from Express (Back-end) using React (Front-end) - node.js

I have an image stored in Express server. I have been trying to download the image using the following method in a React component.
<a href={my_image_url_from_state} download>Download Image</a>
But when i click on the 'Download Image' image is not downloaded. I see this error message:
I am able to display the same image in img tag, ie
<img src={my_image_url_from_state} />
that means nothing wrong with URL.
Do I need to make any changes in Express to download any files?

You need to write a function and invoke it on OnClick of the download button.
<button onClick={() => {download()}}>Download</button>
download function would be:
function download() {
var link = document.createElement('a');
link.href = 'images.jpg';
link.download = '<The URL of the image>';
document.body.appendChild(link);
link.click();
}
working code in reactJS class
class App extends React.Component {
constructor() {
super();
}
render() {
return (
<div>
<img onClick={() => { download(); }} src="<The URL of the image>" />
</div>
);
}
}
function download() {
var link = document.createElement('a');
link.href = 'images.jpg';
link.download = '<The URL of the image>';
document.body.appendChild(link);
link.click();
}

I ended up here because I was looking for a way to download an image in my React app from cloudinary and I found the snippet above as a useful starting point. I was getting the same error as Darshn. I speculate that perhaps the reason it wasn't working is that the backend origin url was different than the client app's origin.
I then researched and found that the download attribute of an anchor element only works for same-origin URLs. See The Anchor element
I then found an article on Soumitra Roy Sarkar's Roy Tutorials that I was able to incorporate into a solution.
I determined that since I was pulling from a different origin I would need to fetch the image, convert it to a Blob, create a URL for it, attach the url to the anchor tag.
Since I already was using the Cloudinary React SDK I was able to add the cloudinary-core to my dependencies.
yarn add cloudinary-core
Then I imported it into my module
import cloudinary from 'cloudinary-core';
const cloudinaryCore = new cloudinary.Cloudinary({cloud_name: '<My Cloudinary Cloud Name>'});
My download function looks like:
function download({publicKey, name}) {
const downloadUrl = `${cloudinaryCore.url(publicKey)}.jpg`;
fetch(downloadUrl)
.then((img) => img.blob())
.then((blob) => {
let url = window.URL.createObjectURL(blob);
let link = document.createElement('a');
link.href = url;
link.download = name;
link.click();
})
}
The function can be called by an html element like this:
<button onClick={() => {download({publicKey: '12345', name: 'image.jpg'})}}>Download</button>

Related

Why React doesn't upload image to server?

I have an app using react and express on the backend and multer to manage the upload. The server side is running properly when I make tests using postman, but if trait to send an image from react the result is unexpected. In that case the file doesn't appear in the uploads folder, however with postman is immediatly.
UploadPage,jsx
const { register, handleSubmit } = useForm();
const onSubmit = async (data) => {
const formData = new FormData();
formData.append('petimage', data.petimage);
try {
const res = await axios.post('/api/petregister', formData);
console.log(res)
} catch (error) {
setError(error.response.data.error);
setTimeout(() => {
setError("");
}, 5000);
}
}
return (
<Container className="mt-5">
<Form onSubmit={handleSubmit(onSubmit)}>
<Form.Group controlId="formFile" className="mb-3">
<Form.Label>Imagen de tu Mascota</Form.Label>
<Form.Control type="file"
label="Select image"
name="petimage"
{...register("petimage")}
/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</Container>
Google Response
The fields with name petimage are the same that I expecified in the backend and used these in the postman tests.
Edit
const store = require('../middlewares/multer');
route.post('/petregister', store.array('petimage', 12), petregister);
The last section of code is the route that is linked with the multer midleware asigned to ssave the images.
When you are making a API call to the backend, it will upload the image to the specific folder that you are defining in the backend like :
const multer = require('multer');
const upload = multer({ dest: 'folder path' });
I think you are getting results unexpected because the name for the image you are giving in formData formData.append('petimage', data.petimage); i.e petimage, it should be the same in the multer fileupload method. You haven't shared the backend code. So, I'm hoping that it may be like this:
var fileUpload = upload.single('petimage'); when the name is the same it will work fine.
If the image is of big size, you can compress it. Please visit this link, it will help you for sure.
https://dev.to/franciscomendes10866/image-compression-with-node-js-4d7h
You can try:
Remove
formData.append('petimage', data.petimage);
and use instead
data.petimage.forEach(pet => formData.append("petimage", pet))
The solution was trait the image as an object. The code is the next:
Object.values(data.petimage).forEach(pet => formData.append('petimage', pet))
Then it worked as expected.

How to know file type of some file uploaded in a form with nodeJS?

I would like to know the type of file obtained with req.files in NodeJS. I need this because the file uploaded has to be a photo for the well work of the app. It not only helps me to check that it is not a .jpg since you can make a .txt and change the extension.
The form is the following:
form(class="form add-form space-down" method="POST" enctype="multipart/form-data")
div.title
h1 UPLOAD NEW PROGRESS
div.form-group
label(for="weight") Weight:
input(type="number" name="weight" class="form-control" placeholder="Enter your weight")
div.form-group
label(for="front") Upload a front photo
input(type="file" name="front" accept="image/*")
div.form-group
label(for="from_side") Upload a from side photo
input(type="file" name="from_side" accept="image/*")
div.form-group
label(for="backwards") Upload a backwards photo
input(type="file" name="backwards" accept="image/*")
And the router handle is the following to obtain the photos uploaded:
routerProgress.post("/home/upload-progress", (req, res) => {
const front = req.files.front;
const from_side = req.files.from_side;
const backwards = req.files.backwards;
}
How can I be sure that front, from_side and backwards are photos?
If anyone has any idea how to do it, I would be very grateful if you could help me.
You can do something like this. Create a function which returns the extension of the file and you can check if it is a valid image extension or not.
routerProgress.post("/home/upload-progress", (req, res) => {
const front = getFileExtension(req.files.front);
const from_side = getFileExtension(req.files.from_side);
const backwards = getFileExtension(req.files.backwards);
if (front) {
// its an image, do something
}
}
function getFileExtension (filename) {
const allowedFileExt = ['JPEG', 'JPG', 'PNG', 'GIF', 'TIFF', 'PSD', 'PDF']; // you can add as per your requirement
const fileExt = /[^.]+$/.exec(filename);
return allowedFileExt.includes(fileExt[0].toUpperCase());
}
If that is the case, you should use mmmagic, it checks the content of the file instead checking only the extension. Try using this lib it will be more useful for your use case. Also, take a look at this npm package image-type

How to display a image from src folder in react app with axios

I'm trying to display many images from my src folder in local react app on a component with axios but i don't know what i have to do to works. If i need to use node.js to do that, show me how please.
Axios code:
I would have to back 3 folders to get in src folder, from this component.
async componentDidMount() { const response = await Axios.get('src/image/'); }
React component's code:
And to show this images i'm using react-slick to render a banner.
<Slider { ...settings }> { response.map((image) => { return ( <img key="" src={image} /> ) })} </Slider>

How can I import an image from the express server to the client (in React)

I'm trying to show an image in react, which is neither a local image (in the client) nor an external image from the web but an image that is in the node.js express server (and I don't want to call it as if it was an external image, because the domain could change and it just doesn't seem right).
I know I can't just import it like I do with a local image in the client because we're speaking about different localhosts. I did try this:
loadImage = async (imageUrl) => {
const response = await fetch(`/api/images/${imageUrl}`);
const data = await response.json();
this.setState({ image: data });
}
componentDidMount() {
const { imageUrl } = this.props;
try {
this.loadImage(imageUrl);
} catch(error) {
console.log("Hay un error: " + error);
}
}
render() {
const { image } = this.state;
return(
<div>
<div>
<img alt="dontknowyet" className="blog-list-image" src={image} // and so on...
{image} does receive the correct path, but the image won't load and the console throws this error:
Not allowed to load local resource: file:///C:/Users/Dafna/Desktop/adrian/proyectos/esteticand/img/t4.jpg
So how can I make it work? and in case that I need to import the image file instead of just the link, how can I do that? (I can't update the state with an image...)
In order to access the path of the image it has to be done through the express server.
For example, if the (backend) server is running on port 4500 and the image is in a folder called images, and the express variable is called app, in the server file you have to use:
app.use(express.static('images'));
and then the image can be accessed in http://localhost:4500/nameoftheimage.jpg.
Do you have the api running on the same port as the React app?
You usually would make them run on different ports. Maybe it's got something to do with it.

Converting blob from database to an image javascript

I have a database, where one of the tables has a blob field and I want to display it as an image. I can't really find any solution for this - any working npm package or a sample of code would be useful. I'm using reactjs and nodejs.
What you want to do is create an URL that you can pass to the img src of the HTML img
JS
var url = window.URL || window.webkitURL;
var imageSrc = url.createObjectURL('your blob');
document.querySelector("#myimage").src = imageSrc;
HTML
<img id="myimage"/>
Method 1 create readable stream and pipe to response
var stream = require('stream');
var bufferStream = new stream.PassThrough();
bufferStream.end(new Buffer( blob, 'binary' ));
res.writeHead(200, {
'Content-Type' : 'image/jpg'
});
// res is standered express res object
bufferStream.pipe(res)
Method 2
pass blob to base64
var bufferBase64 = new Buffer( blob, 'binary' ).toString('base64');
show image
class Example extends React.Component{
render() {
return <img src={"data:image/jpeg;" + bufferBase64} />
}
}
reference
https://stackoverflow.com/.../how-to-create-a-readstream-with-a-buffer-using-nodejs

Resources