I made the routes below in express to download a video from youtube, but the video file goes into my project folder and it is not downloaded into the browser. It's like it works only in localhost.
This is my project folder:
node_modules
src
-app.js
static
-index.html
-styles.css
package.json
'THE FILE DOWNLOADED'
This is the code i made
const express = require('express')
const router = express.Router()
const fs = require('fs')
const ytdl = require('ytdl-core')
const youtube = require('simple-youtube-api')
router.post('/downloadByUrl', async (req, res) => {
let url = req.body.url
await downloadVideo(url)
return res.redirect('/')
})
router.post('/downloadByName', async (req, res) => {
let videoName = req.body.videoName
let youtubeClient = new youtube('MY YOUTUBE API KEY')
await youtubeClient.searchVideos(videoName, 1)
.then(results => {
downloadVideo('https://youtu.be/' + results[0].id)
})
.catch(console.log)
return res.redirect('/')
})
function downloadVideo(URL) {
let videoReadableStream = ytdl(URL)
ytdl.getInfo(URL, (err, info) => {
let title = info.title.replace('|', '').toString('ascii')
let stream = videoReadableStream.pipe(fs.createWriteStream(title + '.mp4'))
stream.on('finish', () => {
console.log('Finished')
})
})
}
module.exports = router
I want to host my website so i want to make possible the download of the file into the 'download' section of the browser.
How can I do that?
Related
I would like to scrape multiple websites using NodeJS, Express, Cheerio and Axios.
I'm able now to scrape 1 website and display the information to the HTML.
But when I try to scrape multiple websites looking for the same element, it doesn't go through the forEach (stops after 1 cycle). Notice my loop which doesn't work correctly:
urls.forEach(url => {
2 files that are the most important:
index.js
const PORT = 8000
const axios = require('axios')
const cheerio = require('cheerio')
const express = require('express')
const app = express()
const cors = require('cors')
app.use(cors())
const urls = ['https://www.google.nl','https://www.google.de']
// const url = 'https://www.heineken.com/nl/nl/'
app.get('/', function(req, res){
res.json('Robin')
})
urls.forEach(url => {
app.get('/results', (req, res) => {
axios(url)
.then(response => {
const html = response.data
const $ = cheerio.load(html)
const articles = []
$('script', html).each(function(){
const link = $(this).get()[0].namespace
if (link !== undefined) {
if (link.indexOf('w3.org') > -1) {
articles.push({
link
})
}
}
})
res.json(articles)
}).catch(err => console.log(err))
})
})
app.listen(PORT, () => console.log('server running on PORT ${PORT}'))
App.js:
const root = document.querySelector('#root')
fetch('http://localhost:8000/results')
.then(response => {return response.json()})
.then(data => {
console.log(data)
data.forEach(article => {
const title = `<h3>` + article.link + `</h3>`
root.insertAdjacentHTML("beforeend", title)
})
})
You're registering multiple route handlers for the same route. Express will only route requests to the first one. Move your URL loop inside app.get("/results", ...)...
app.get("/results", async (req, res, next) => {
try {
res.json(
(
await Promise.all(
urls.map(async (url) => {
const { data } = await axios(url);
const $ = cheerio.load(data);
const articles = [];
$("script", html).each(function () {
const link = $(this).get()[0].namespace;
if (link !== undefined) {
if (link.indexOf("w3.org") > -1) {
articles.push({
link,
});
}
}
});
return articles;
})
)
).flat() // un-nest each array of articles
);
} catch (err) {
console.error(err);
next(err); // make sure Express responds with an error
}
});
I've been trying to use the node-lame library to encode a file from the uploaded bitrate to 32 kbps to save space the same way I do it with sharp to compress my images.
My code first checks if the file is an audio file. If it is it then makes the encoder and it should encode it:
if (aud.test(user_file)){
const encoder = new Lame({
"output": req.file.path,
"bitrate": 32,
}).setFile(req.file.path);
await encoder
.encode()
.then(() => {})
.catch((error) => {
// Something went wrong
});
}
The problem is that it doesn't actually get encoded. I have also tried this in my .then but it doesn't help.
.then(data => {
fs.writeFileSync(req.file.path + '.mp3', data);
user_file = user_file + '.mp3';
fs.unlinkSync(req.file.path)
})
This is supposed to be a fairly simple library so I don't know what I'm doing wrong. I am trying to encode from file to file.
Also tried this:
const encoder = new Lame({
"output": user_file + '.mp3',
"bitrate": 32,
}).setFile(req.file.path);
I went ahead and wrote a demo for this. You can find the full repo here. I have verified this does work but keep in mind this is only a proof of concept.
This is what my Express server looks like:
const express = require('express');
const fs = require('fs');
const path = require('path');
const fileUpload = require('express-fileupload');
const Lame = require('node-lame').Lame;
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(fileUpload());
// File upload path
app.post('/upload', async (req, res) => {
const fileToEncode = req.files.uploadedFile;
if (!fileToEncode) {
res.status(500).end();
return;
}
const filePath = path.resolve('./uploads', fileToEncode.name);
const outputPath = path.resolve('./uploads', fileToEncode.name + '-encoded.mp3');
// Save uploaded file to disk
await fileToEncode.mv(filePath);
try {
const encoder = new Lame({
output: outputPath,
bitrate: 8,
}).setFile(filePath);
await encoder.encode();
res.download(outputPath);
} catch (encodingError) {
console.error(encodingError);
res.status(500).send(encodingError);
}
// Removed files we saved on disk
res.on('finish', async () => {
await fs.unlinkSync(filePath);
await fs.unlinkSync(outputPath);
})
});
// Home page
app.get('*', (req, res) => {
res.status(200).send(`
<!DOCTYPE html>
<html>
<body>
<p id="status"></p>
<form method="post" enctype="multipart/form-data" action="/upload" onsubmit="handleOnSubmit(event, this)">
<input name="uploadedFile" type="file" />
<button id="submit">Submit Query</button>
</form>
<script>
async function handleOnSubmit(e,form) {
const statusEl = document.getElementById("status");
statusEl.innerHTML = "Uploading ...";
e.preventDefault();
const resp = await fetch(form.action, { method:'post', body: new FormData(form) });
const blob = await resp.blob();
const href = await URL.createObjectURL(blob);
Object.assign(document.createElement('a'), {
href,
download: 'encoded.mp3',
}).click();
statusEl.innerHTML = "Done. Check your console.";
}
</script>
</body>
</html>
`);
});
process.env.PORT = process.env.PORT || 3003;
app.listen(process.env.PORT, () => {
console.log(`Server listening on port ${process.env.PORT}`);
});
I have a page where i display a static image. I want to display there the image i just uploaded. For that I have implemented file upload using express-fileUpload on my server and its working fine, it gets the photo and saves it into a folder, but i cannot display it on my page ( after page refresh it disappears).
How could i make this image remain even after refresh?
server.js code:
require('dotenv').config()
const express = require('express')
const mongoose = require('mongoose')
const cookieParser = require('cookie-parser')
const Years = require('./models/yearModel')
const Users = require('./models/userModel')
const bcrypt = require("bcrypt");
const cors = require('cors')
const fileUpload = require('express-fileupload')
const app = express()
app.use(express.json())
app.use(cors())
app.use(cookieParser())
app.use(fileUpload())
app.post('/upload', (req, res) => {
if (req.files === null) {
return res.status(400).json({ msg: 'No file uploaded' });
}
const file = req.files.file;
const fileName = "orar.jpg"+file.name
file.mv(`${__dirname}/client/public/uploads/${fileName}`, err => {
if (err) {
console.error(err);
return res.status(500).send(err);
}
res.json({ fileName: file.name, filePath: `/uploads/${file.name}` });
});
});
const PORT = process.env.PORT || 5000
app.listen(PORT, () => {
console.log('Server running on port: ', PORT)
})
And this is the code of my page where i want to display the image ( without return markup ):
const Orar = () => {
const [file, setFile] = useState("");
const [filename, setFilename] = useState("Choose File");
const [uploadedFile, setUploadedFile] = useState({});
const onChange = (e) => {
setFile(e.target.files[0]);
setFilename(e.target.files[0].name);
};
const onSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("file", file);
try {
const res = await axios.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
}
});
const { fileName, filePath } = res.data;
setUploadedFile({ fileName, filePath });
setMessage("File Uploaded");
} catch (err) {
if (err.response.status === 500) {
setMessage("There was a problem with the server");
} else {
setMessage(err.response.data.msg);
}
}
};
I don't know the architecture of your website and database so if you stock only one image inside your website, you can store the id of the uploaded image inside your browser localstorage: link here (I don't recommend this practice).
A good practice is to store the id or path of your uploaded image inside your database. For example, if you want to edit or display a profile picture, go to your user schema (in your db), stock the id or the path of the uploaded image here. Then when you'll call the #GET /users/:userId, you will retrieve the image id or the image path (depends on which of them you stock).
If you retrieve the link to your image, just do that inside your html code:
<img src={myImageLocation} alt={myImageAlt} />
If you stock your image on a dedicated server (it's what you're doing), you can only retrieve the image id and render the image like that:
<img src={`${serverPATH}/${imageId}`} alt={myImageAlt} />
I have stored the file after uploading it to the downloads folder in my project directory.
I want to download that saved file from the frontend.
When I click on the download button, it doesn't fetch the file.
And when I go to http://localhost:5000/download on the express app, I got this error message
Error: Can't set headers after they are sent.
Express Server Code:
app.get('/download', (req, res) => {
res.send('file downloaded')
const file = './downloads/output.yml';
res.download(file, 'openapi.yml', (err) => {
if (err) {
console.log(err)
} else {
console.log('file downloaded')
}
});
});
Frontend App code:
HTML:
<button class="download-btn">download</button>
Script:
const handleDownload = async () => {
const res = await fetch("https://cors-anywhere.herokuapp.com/http://localhost:5000/download");
const blob = await res.blob();
download(blob, 'output.yml');
}
downloadBtn.addEventListener('click', handleDownload);
Folder Structure:
Update:
Server.js
const uploadFiles = async (req, res) => {
const file = await req.files[0];
console.log(file)
postmanCollection = file.path;
outputFile = `downloads/${file.filename}.yml`
convertCollection();
res.json({ message: "Successfully uploaded files" });
}
app.post("/upload_files", upload.array("files"), uploadFiles);
Anyone please help me with this.
You are already using res.send ,which sends the response headers back to client ,which ends the request response cycle ,and when you try to do res.download it throws error. Use instead
app.get('/download', (req, res) => {
const file = './downloads/output.yml';
res.download(file, 'openapi.yml', (err) => {
if (err) {
console.log(err)
} else {
console.log('file downloaded')
}
});
});
res.send('file downloaded')--->remove this line
You need to update your js code as well
const handleDownload = async () => {
const res = await fetch("https://cors-anywhere.herokuapp.com/download"); //http://localhost:5000--->this is not required
const blob = await res.blob();
download(blob, 'output.yml');
}
downloadBtn.addEventListener('click', handleDownload);
Can't upload files to the server. I've used npm install express-fileupload and also did the var fileUpload = require('express-fileupload') and app.use(fileUpload()). And while calling
router.post('/add-products',(req,res)=>{
console.log(req.body);
console.log(req.files.image);
It says cannot read image of null.
I can give you a working full example.
Project structure:
- storage (empty folder)
- routes
-> upload.route.js
- controllers
-> upload.controller.js
index.js
index.js
const express = require('express');
const app = express();
const route = require('./routes/upload.route');
app.use('/', route);
let port = 8000;
app.listen(port);
console.log(`API listens localhost:${port}`);
This is your upload.route.js
const express = require('express');
const router = express.Router();
const { uploadController } = require('../controllers/upload.controller');
router.use('/media/upload', uploadController);
module.exports = router;
This is upload.controller.js
const formidable = require('formidable');
const path = require('path');
exports.upload = async (req, res) => {
try {
// Receive the media and store it
let [uploadPath, filename] = await processUpload(req);
return res
.status(200)
.send({
success: 1,
message: "File uploaded",
filename,
uploadPath
});
} catch (error) {
return res
.status(400)
.send({
success: 0,
message: "Ops! Something went wrong",
errorObject: error.message
});
}
}
function processUpload(req) {
return new Promise((resolve, reject) => {
try {
let uploadDir = __dirname + `/../storage`;
// We used helper formidable package
let form = new formidable.IncomingForm()
form.multiples = true;
form.keepExtensions = true;
// Upload path
form.uploadDir = uploadDir;
let result;
form.on('fileBegin', function (name, file) {
if (!file.type) reject(new Error("No media specified!"));
const fileExt = path.extname(file.name);
let filename = "test" + fileExt;
file.path = path.join(uploadDir, filename);
// Return the path where file uploaded
result = [file.path, uuid];
});
form.parse(req, (err, fields, files) => {
if (err) return reject("Upload failed.");
resolve(result);
});
} catch (error) {
reject("Upload failed.");
}
});
}
When you call localhost:8000/media/upload with a POST or PUT request with postman form-data. You can see the uploaded file under the storage folder in the project.
Let me know if something goes wrong with the code
Note: You need to use formidable (For uploading) package to run the example