I have a react app in which I have a button. Basically a div.
This is returned from my Button Component and everything else are props.
<button className={"button "+styleButton} onClick={handleClick}>
{source && <img src={source} alt={alt} className={"image "+styleImage} /> }
<h3 className="text">{children}</h3>
</button>
Now what I did when someone clicks this Button(div) is :
const handleClick = () => {
console.log('downloading...');
let a = document.createElement('a');
a.href = '192.168.43.102/download/brief';
a.download = 'brief.pdf';
a.click();
}
On clicking on that div, I want a pdf to be downloaded on the client side. There are hundreds to ways on the internet. Some send a request with(axios) and download on front-end and then create a downloadable link and some just use an anchor tag.
I tried some of these but I can't make it working. Also on the express side.
This route is my express side:
const router = require('express').Router();
const { Router } = require('express');
const path = require('path');
const { route } = require('./api');
const brief = path.join(__dirname, '..', '/public/resources/krishi-brief.pdf');
const uml = path.join(__dirname, '..', '/public/resources/krishi-uml.pdf');
router.get('/brief', (req, res) => {
res.sendFile(brief);
});
router.get('/uml', (req, res) => {
res.download(uml);
});
router.get('/proposal', (req, res) => {
res.send("Not found");
});
router.get('/ppt', (req, res) => {
res.send("Not found");
});
module.exports = router;
I have a very good pfd which is non-corrupted but when I get the file, it is corrupted because none of the applications can open it.
I also tried:
router.get('/uml', (req, res) => {
res.set({
'Content-Type': 'application/pdf'
})
res.download(uml);
});
But now I am confused about the implementation and also if this is the right way.
Please tell me if this is the right(professional) way to do this in react app and where is it wrong? I am only getting corrupted file :(
OK so when I click on the download, the server 192.168.43.102 should be written as http://192.168.43.102 and the anchor tag won't give or throw and error but just download something which I am not sure about which is not even on your route.
Basic thing I was struggling on.
Related
Hello sorry for my english, i have a little problem. i try to upload many images but in back side i have just one image, (i use React express formidable cloudinary) here is my code front :
const [arrayFiles, setArrayFiles] = useState([]);
const handleFiles = (e) => {
let arrayUpload = [...arrayFiles];
arrayUpload.push(e.target.files[0]);
setArrayFiles(arrayUpload);
};
const handleSubmit = async (e) => {
arrayFiles.forEach((file) => {
formData.append("image", file);
});
const response = await axios.post(
"http://localhost:3100/offer/publish",
formData
);
here is my code back but req.files => just one image
my page route :
router.post("/offer/publish", async (req, res) => {
console.log(req.files);
const result = await cloudinary.uploader.upload(req.files.image.path, {
folder: `api/leboncoin/offers/${newOffer._id}`, // _id vient de la création du newOffer au dessus
public_id: "preview",
cloud_name: process.env.CLOUDINARY_NAME,
});
my page index.js:
page index.js :
const express = require("express");
const formidable = require("express-formidable");
const mongoose = require("mongoose");
const cloudinary = require("cloudinary").v2;
const cors = require("cors");
const app = express();
app.use(cors());
app.use(formidable({ multiples: true }));
You only get one file in req.file as you've set your multer.single
Using multer
There are 3 ways you can handle multiple file upload, each with a slightly different taste.
Assume you have a base multer
const storage = multer.diskStorage({
destination: "public/data/",
filename: function(req, file, cb){
// You may change this to however you want, only affect your file name
crypto.randomBytes(20, (err, buf) => {
cb(null, buf.toString("hex") + path.extname(file.originalname))
})
}
});
const upload = multer({ storage: storage });
Use .any()
Accepts all files that comes over the wire. An array of files will be stored in req.files.
WARNING: Make sure that you always handle the files that a user uploads. Never add multer as a global middleware since a malicious user could upload files to a route that you didn't anticipate. Only use this function on routes where you are handling the uploaded files.
router.post("/offer/publish",upload.any(), async (req, res) => {
console.log(req.files); // Should give you an array of files
// Do anything else
});
Use .array(fieldname[, maxCount])
Accept an array of files, all with the name fieldname. Optionally error out if more than maxCount files are uploaded. The array of files will be stored in req.files.
router.post("/offer/publish",upload.array('someFieldName', 10), async (req, res) => {
console.log(req.files); // Should give you an array of files
// Do anything else
});
Use .fields(fields)
Accept a mix of files, specified by fields. An object with arrays of files will be stored in req.files.
fields should be an array of objects with name and optionally a maxCount. Example:
router.post(
"/offer/publish",
upload.fields([
{
name: "image",
maxCount: 1,
},
{
name: "audio",
maxCount: 1,
},
]),
async (req, res) => {
console.log(req.files.image[0]);
console.log(req.files.audio[0]);
// Do anything else
}
);
For your case, I would recommend going with Option 2.
so the problem i'm having is, there is a directory in my public_html file named blog that is not related to my nextJs app, so basically after i deploy the app on host, everything works fine until i choose to check my blog part, for example the url below:
www.sth.com/blog
when i get there, i get the default next 404 page, the problem is, there is actual pages there, and i want my nodeJs server to ignore that exact route so when user goes to www.sth.com/blog, node app kind of ignore it and let it load the basic html pages.
i think it has something to do with my server.js file so here's the code in server.js
also i hosted the app on cpanel if that's important.
const { createServer } = require('http')
const next = require('next')
const isDevMode = process.env.NODE_ENV !== 'production'
const port = process.env.PORT ? process.env.PORT : 3000
const nextjsApp = next({ dev: isDevMode })
const nextjsRequestHandler = nextjsApp.getRequestHandler()
nextjsApp
.prepare()
.then(() => {
createServer((req, res) => {
const url = new URL(req.url, "http://w.w")
nextjsRequestHandler(req, res, url)
}).listen(port, (err) => {
if (err) throw err
})
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
thank you in advance.
In your server configuration you have to check if request is meant for next or not. If not - you need to respond with your page, instead of passing request further to next:
(req, res) => {
const url = new URL(req.url, "http://w.w");
if (/^\/blog\//.test(url.pathname)) {
// send response with blog
} else {
// pass everything to Next
nextjsRequestHandler(req, res, url);
}
};
Another option would be to split next and not next in two different parts and route requests to them through reverse proxy.
I have a file stored on an external server. I want to be able to call GET request to my own NodeJS server (using express). What I'm currently doing is almost OK, but it does not trigger browser to download the file (no browser UI for the download is shown):
const express = require('express');
const app = express();
app.get('/download-file', (req, res) => {
const externalRequest = http.request({
hostname: 'my.external-server.com',
path: '/my/path/my-file.zip',
}, (externalRes) => {
res.setHeader('Content-Disposition', 'attachment; filename="MyFile.zip"');
externalRes.pipe(res);
});
return externalRequest.end();
});
app.listen(8080, () => console.log('Server is listening'));
What am I missing here? I see that triggering a GET request to localhost:8080/download-file is actually fetching it, but no UI for download is shown.
This is the code that is running in one of my pet projects, hope it helps.
It pipes the download request ok, but there is no size info for the download, so it becames one of that downloads that you dont know when will finish.
const http = require('http')
app.get('/down_file/:file_name', (req, res) => {
const fileName = req.params.file_name
const url = "http://externalUrl/" + fileName
var externalReq = http.request(url, function(externalRes) {
res.setHeader("content-disposition", "attachment; filename=" + fileName);
externalRes.pipe(res);
});
externalReq.end();
})
I'm new both to Express.js and to StackOverflow; I'm sorry if this is a duplicate question. I checked, but didn't see anything relevant.
So, I'm using Multer + Express to allow a user to upload an image, which will be named '${username}.{extension}', to a server-side /uploads/ folder. I don't want users to be able to save more than one image on the server (i.e. no "user1.jpg" and "user1.png"). To accomplish this, I wrote the following middleware:
function deleteUserImage(req){
const acceptedExtensions = ['.png', '.jpg', '.jpeg', '.tif', '.tiff', '.JPG', '.bmp'];
acceptedExtensions.forEach(char => {
if(fs.existsSync(`./uploads/${req.cookies.username+char}`)){
fs.unlinkSync(`./uploads/${req.cookies.username+char}`);
}
})
}
I then was able to get the functionality I wanted with the following routes:
app.post('/process_upload-image', (req, res, next) => { //User sends post req w/ image file
deleteUserImage(req) //images for that user are cleared.
next();
})
app.post('/process_upload-image', upload.single('user-image'), (req, res, next) => {
res.redirect('/welcome'); //user is redirected after multer uploads the image.
})
I was wondering if this was best-practices, however, since you end up with two routes listening at the same URI? Is there a way to pass req to deleteUserImage(), then call upload.single()...all in one route?
Thanks!
You can simply chain multiple middlewares on one route. Change the deleteUserImage function to:
function deleteUserImage(req, res, next){
const acceptedExtensions = ['.png', '.jpg', '.jpeg', '.tif', '.tiff', '.JPG', '.bmp'];
acceptedExtensions.forEach(char => {
if(fs.existsSync(`./uploads/${req.cookies.username+char}`)){
fs.unlinkSync(`./uploads/${req.cookies.username+char}`);
}
})
next()
}
and then remove the first route and change the second one to:
app.post('/process_upload-image', deleteUserImage, upload.single('user-image'), (req, res, next) => {
res.redirect('/welcome');
})
I am writing store and i have a problem with rendering.
I want to have subdomain /category and it's working just find for route like tis
const CategoryOne = ((req, res) =>{
res.render('shop/category');
});
router.get('/category', CategoryOne);
This is working perfect, but when i go on subdomain category/shoes i want to be redirect on /category with parametr shoes
const Category = ((req, res) =>{
const categoryPass = req.params.category;
res.render('shop/category', {
category: categoryPass
});
});
router.get('/category/:category', Category);
and it's not working, should i redirect? When i do it
res.redirect('/category')
Then i dont have category parametr
EDIT:
What i have done so far:
const CategoryOne = ((req, res) =>{
const passedCategory = req.session.categorypassed;
req.session.categorypassed = undefined;
console.log(passedCategory);
Product.find((err, response) => {
res.render('shop/category', {
title: 'Test', products: response, category: passedCategory
});
});
});
const Category = ((req, res) =>{
req.session.categorypassed = req.params.category;
res.redirect('/category');
});
The problem is, when i refresh page i dont have this paramets, is there any way to save it?
A better way to handle this is to use a central public directory for your assets and request them with an absolute path, such as /assets/images/logo.png, instead of a relative path, such as assets/images/logo.png.
You can read more about relative and absolute paths here