I want redirect my page after a res.download, even i try put res.redirect in end of my app, they send this error: Cannot set headers after they are sent to the client.
Have some way to redirect before a res.download?
The code for my app:
const express = require("express"),
router = express.Router(),
ytdl = require('ytdl-core'),
cp = require('child_process'),
ffmpeg = require('ffmpeg-static'),
fs = require('fs'),
crypto = require('crypto');
module.exports = (router) => {
//Tokens
const inUseTokens = {};
router.get('/', (req, res) =>{
if (req.query.urlfail){
res.render('index', {message: "URL Inválida, verifique sua URL e tente novamente!"})
}else if(req.query.private){
res.render('index', {message: "Esse vídeo é privado ou não foi encontrado."})
}else{
res.render('index')
}
})
router.post('/getlink', (req, res) => {
const ref = req.body.url;
console.log(ytdl.validateURL(ref))
if(ytdl.validateURL(ref) == true){
ytdl.getInfo(ref).then((info) => info).then(async info => {
let title = info.videoDetails.title,
thumbnail = info.player_response.videoDetails.thumbnail.thumbnails[3].url,
seconds = info.player_response.videoDetails.lengthSeconds,
formatos = [],
qualidades = {};
const itagMP4 = [137, 136, 135, 134],
itagWEBM = [248, 247];
for(i = 0; i < info.formats.length; i++){
formatos.push(info.formats[i])
qualidades = Object.assign(formatos);
}
const mp4Keys = Object.keys(qualidades),
mp4Todos = mp4Keys.map((keys) => qualidades[keys]),
mp4Filtrados = mp4Todos.filter((item) => itagMP4.includes(item.itag)),
Formatar18 = mp4Todos.filter((item) => item.itag === 18),
webmKeys = Object.keys(qualidades),
webmTodos = webmKeys.map((keys) => qualidades[keys]),
webmFiltrados = webmTodos.filter((item) => itagWEBM.includes(item.itag));
Formatar18[0].qualityLabel = '144p'
mp4Filtrados.push(Formatar18[0])
res.render('download', {thumbnail: thumbnail, title: title, seconds: seconds, mp4: mp4Filtrados, webm: webmFiltrados, url: ref})
}).catch((err) => {
res.redirect('/?private=true')
console.log("Erro: " + err)
});
}else{
res.redirect('/?urlfail=true')
}
})
router.post('/download', (req, res) => {
if (req.body.qualidade == 137 || req.body.qualidade == 136 || req.body.qualidade == 135 || req.body.qualidade == 134){
var formato = ".mp4"
} else {
var formato = ".webm"
}
const token = crypto.randomBytes(20).toString('hex');
// Adicionar token ao objeto
inUseTokens[token] = true;
// Verificar se o token é válido
if (!inUseTokens[token]) {
res.status(401).send('Unauthorized');
return;
}
// Obter URL do vídeo
const url = req.body.url,
titulo = req.body.titulo.replace('/', '');
// Obter audio e video
const audio = ytdl(url, { quality: 'highestaudio' }),
video = ytdl(url, { quality: req.body.qualidade });
// Start the ffmpeg child process
const ffmpegProcess = cp.spawn(ffmpeg, [
// Remove ffmpeg's console spamming
'-loglevel', '8', '-hide_banner',
// Redirect/Enable progress messages
'-progress', 'pipe:3',
// Set inputs
'-i', 'pipe:4',
'-i', 'pipe:5',
// Map audio & video from streams
'-map', '0:a',
'-map', '1:v',
// Keep encoding
'-c:v', 'copy',
// Define output file
token + formato,
], {
windowsHide: true,
stdio: [
/* Standard: stdin, stdout, stderr */
'inherit', 'inherit', 'inherit',
/* Custom: pipe:3, pipe:4, pipe:5 */
'pipe', 'pipe', 'pipe', 'pipe'
],
});
ffmpegProcess.stdio[3].on('data', () => {
console.log("convertendo...")
});
audio.pipe(ffmpegProcess.stdio[4]);
video.pipe(ffmpegProcess.stdio[5]);
ffmpegProcess.stdio[6].on('error', (err) => {
// Remover token do objeto
delete inUseTokens[token];
res.status(500).send(err.message);
});
ffmpegProcess.stdio[6].on('close', () => {
console.log(titulo)
res.set({
'Content-Type': 'text/plain',
'Location': '/'
});
res.download(`${token}${formato}`, `download.com.br_${titulo}${formato}`, (err) => {
if (err) {
console.log(err)
// Remover token do objeto
delete inUseTokens[token];
// Apagar arquivo baixado
fs.unlink(`${token}${formato}`, (err) => {
if (err) console.log(err);
console.log('arquivo deletado')
});
} else {
// Remover token do objeto
delete inUseTokens[token];
// Apagar arquivo baixado
fs.unlink(`${token}${formato}`, (err) => {
if (err) console.log(err);
console.log('arquivo deletado')
});
}
})
})
});
}
I tried put res.set({'Content-Type': 'text/plain', 'Location': '/'}); but don't redirect...
Related
I'm working with MQTT.js and Mosquitto as my broker.
I'm trying to connect to my broker on the POST method and it works, storing the "Client" on the variable.
The problem is that the code doesn't work on the GET where my client isn't connecting and therefore, I can't subscribe or message to my topics.
I don't know what is the reason for that, but I solved it when I do de client connection inside the GET, but in this case, I don't want to do the connection inside the GET.
That's because the POST response is given when I login in the platform, and if I do the client connection on the GET that is for see the variables, always that I recharge the page It will connect again and then it will subscribe to the same topic, and after that when I send one message, if is subscribed three times to the same topic, it will Post 3 times.
PD: When I print the client inside the GET, It prints the same value that is assigned in the POST
const express = require("express");
const router = express.Router();
const request = require("request");
let mqtt = require('mqtt');
require("../requestMethods/get.js")();
require("../requestMethods/post.js")();
let client;
let options;
const postMqttData = async (string, body) => {
const url = `http://localhost:8000/${string}`;
const data = await postData(url, body)
console.log(data)
}
const getToken = async (string) => {
const url = `http://localhost:3000/api/tokenuser/${string}`
const data = await requestData(url)
const token = data;
//console.log(token)
return token;
}
router.post("/apiClientBroker", (req, res,next) => {
let {user} = req.body;
console.log(user)
getToken(user).then(meta => {
const { value } = meta.token[0];
options = { username: user, password: value, clean: true, keepAlive: 60 }
client = mqtt.connect('mqtt://localhost', options);
//console.log(client)
})
})
router.get("/:user/:project",(req, res, next) => {
// clientBrokerResponse(req.client)
// console.log(client)
const { user } = req.params;
const { project } = req.params;
const uri = hostURL + "/variables/" + user + "/" + project;
request.get(uri, (err, resp, body) => {
//body = JSON.parse(body);
if (err || resp.status == 500) {
res.status(500).send({ ERROR: "Error searching" });
} else {
if (body.length) {
const json= JSON.parse(body);
console.log(client)
client.on('connect', function () {
for(let i= 0; i<json.length;i++){
const {deviceN}= json[i];
const {variableN}= json[i];
const {variableT}=json[i];
const topico = `${user}/${project}/${deviceN}/${variableN}`;
if(variableT==='Independiente'){
client.subscribe(topico, function (err) {
console.log(`suscrito a ${topico}`)
if (err) {
console.log("error en la subscripcion")
}
})
}
}
})
client.on('message', function (topic, message) {
// message is Buffer
json1 = JSON.parse(message.toString()); //de esta manera se convierte el mensaje recibido en un json
console.log(json1);
// if (json1.token === value) {
json2 = { 'value': json1.value }
postMqttData(`${topic}/${json1.token}`, json2)
// } else {
// console.log("Se está enviando a otro usuario el dato")
// }
})
res.status(resp.statusCode).send(body);
} else {
res
.status(404)
.send({ message: "Variables not found for this project" });
}
}
});
});
I am creating the backend of an ecommerce store and I have to validate the photos. I started with an idea, but my teacher has made me change and now I don't know how to combine what I have done with what he asks of me. Let me explain the situation: I have created a special path for uploading the image to cloudinary. This is the code:
const router = require('express').Router()
const cloudinary = require('cloudinary')
const auth = require('./../middleware/auth')
const authAdmin = require('./../middleware/authAdmin')
const fs = require('fs-extra')
// we will upload image on cloudinary
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.CLOUD_API_KEY,
api_secret: process.env.CLOUD_API_SECRET
})
// Upload image only admin can use
router.post('/upload', auth, authAdmin, (req, res) =>{
try {
if(!req.files || Object.keys(req.files).length === 0)
return res.status(400).json({msg: 'No files were uploaded.'})
const file = req.files.file;
if(file.size > 1024*1024) {
removeTmp(file.tempFilePath)
return res.status(400).json({msg: "Size too large"})
}
if(file.mimetype !== 'image/jpeg' && file.mimetype !== 'image/png'){
removeTmp(file.tempFilePath)
return res.status(400).json({msg: "File format is incorrect."})
}
cloudinary.v2.uploader.upload(file.tempFilePath, {folder: "ecommerce"}, async(err, result)=>{
if(err) throw err;
removeTmp(file.tempFilePath)
res.json({public_id: result.public_id, url: result.secure_url})
})
} catch (err) {
return res.status(500).json({msg: err.message})
}
})
// Delete image only admin can use
router.post('/destroy',auth , authAdmin, (req, res) =>{
try {
const {public_id} = req.body;
if(!public_id) return res.status(400).json({msg: 'No images Selected'})
cloudinary.v2.uploader.destroy(public_id, async(err, result) =>{
if(err) throw err;
res.json({msg: "Deleted Image"})
})
} catch (err) {
return res.status(500).json({msg: err.message})
}
})
const removeTmp = (path) =>{
fs.unlink(path, err=>{
if(err) throw err;
})
}
module.exports = router
I have a product model made like this:
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name:{
type: String,
trim: true,
required: true
},
price:{
type: Number,
trim: true,
required: true
},
description:{
type: String,
required: true
},
images:{
type: Object,
required: true
},
category:{
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
required: true
},
}, {
timestamps: true
})
module.exports = mongoose.model("Products", productSchema)
And this is my function to create the product:
createProduct: async (req, res) => {
try {
const {
name,
price,
images,
description,
categoryId
} = req.body;
if (!images) return res.status(400).json({
message: "No hay imagen del producto!"
})
if (!(name || price || description))
return res.status(400).json({
message: "Por favor, complete todos los campos"
})
const product = await Products.findOne({
name
})
if (product)
return res.status(400).json({
message: "Este producto ya ha sido creado anteriormente"
})
const newProduct = new Products({
name: name.toLowerCase(),
price,
description,
categoryId,
images
})
await newProduct.save()
res.json({
message: "Producto creado ! "
})
} catch (err) {
return res.status(500).json({
message: err.message
})
}
},
And this is the route for create product :
app.use('/api', require('./routes/productRouter'))
router.post('/products', auth, authAdmin, productCtrl.createProduct)
The point is that, first, I would upload the image, and when creating the product, it would pass through Postman the results of the image upload (public_id and url).
In the review, my teacher told me to put everything together in the same route, that of creating the product, I have been trying all morning and there is no way it will work for me. Can somebody help me ? I don't know exactly what the function should look like with the integrated image.
Kind regards, thank you in advance
// Create Product -- Admin
exports.createProduct = catchAsyncErrors(async (req, res, next) => {
let images = [];
if (typeof req.body.images === "string") {
images.push(req.body.images);
} else {
images = req.body.images;
}
const imagesLinks = [];
for (let i = 0; i < images.length; i++) {
const result = await cloudinary.v2.uploader.upload(images[i], {
folder: "products",
});
imagesLinks.push({
public_id: result.public_id,
url: result.secure_url,
});
}
req.body.images = imagesLinks;
req.body.user = req.user.id;
const product = await Product.create(req.body);
res.status(201).json({
success: true,
product,
});
});
I am using multer to upload a file to my backend. Here is the route:
// #route POST api/users/resume
// #desc Upload user resume
// #access Private
router.post("/resume", auth, async (req, res) => {
try {
let resumeBackupName = null;
let resumeMatrix = {
"application/vnd.ms-excel": "xls",
"application/msword": "doc",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
"docx",
"application/pdf": "pdf"
};
let user = await User.findById({ _id: req.user.id.toString() });
if (
user.resume &&
user.resume.extension &&
user.resume.extension.length > 0
) {
resumeBackupName =
req.user.id.toString() +
"-backup-" +
user.resume.extension.length.toString() +
"." +
user.resume.extension[user.resume.extension.length - 1];
user.resume.backup.push(resumeBackupName);
}
let date = new Date();
let resumeMimeType = null;
let resumeExtension = null;
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public");
},
filename: (req, file, cb) => {
cb(
null,
req.user.id.toString() +
"-" +
date.getTime() +
"-" +
file.originalname
);
}
});
const upload = multer({ storage: storage }).single("file");
upload(req, res, err => {
resumeMimeType = req.file.mimetype;
resumeExtension = resumeMatrix[resumeMimeType];
user.resume.mimeType = resumeMimeType;
user.resume.extension.push(resumeExtension);
if (err instanceof multer.MulterError) {
return res.status(500).json(err);
} else if (err) {
return res.status(500).json(err);
}
});
await user.save();
return res.json({ msg: "Success!" });
} catch (err) {
console.log(err);
res.status(500).send("Server error");
}
});
The problem is within the upload function. Console logging user after the user.resume.mimeType = resumeMimeType; and user.resume.extension.push(resumeExtension); lines properly displays the values, but when await user.save() is hit, it doesn't save anything. Is it a scoping issue? I'm getting a warning variables declared but never used for let resumeMimeType = null; and let resumeExtension = null;, but they are used in the upload function. Not sure what I'm doing wrong.
I am creating file upload functionality on Cloud Storage. Here's the backend code:
const Storage = require('#google-cloud/storage')
const storage = Storage({
projectId: 'a-1485'
})
const bucket = storage.bucket('a-1485.appspot.com')
function getPublicUrl (filename) {
return 'https://storage.googleapis.com/a-1485.appspot.com/${filename}'
}
function sendUploadToGCS (req, res, next) {
if (!req.file) {
return next()
}
const gcsname = Date.now() + req.file.originalname
console.log(gcsname)
const file = bucket.file(gcsname)
const stream = file.createWriteStream({
metadata: {
contentType: req.file.mimetype
}
})
stream.on('error', (err) => {
req.file.cloudStorageError = err
next(err)
})
stream.on('finish', () => {
req.file.cloudStorageObject = gcsname
req.file.cloudStoragePublicUrl = getPublicUrl(gcsname)
next()
})
stream.end(req.file.buffer);
}
module.exports = {
getPublicUrl,
sendUploadToGCS
}
In my app.js file:
app.post('/upload-image', multer.single('image'), images.sendUploadToGCS, (req, res, next) => {
let data = req.body
console.log(data)
if (req.file && req.file.cloudStoragePublicUrl) {
data.imageUrl = req.file.cloudStoragePublicUrl
}
getModel().create(data, (err, savedData) => {
if (err) {
next(err)
return
}
res.redirect(`${req.baseUrl}/${savedData.id}`);
})
}
)
However, when I upload an image I get an error thrown saying 'getModel()' is not defined. But as you can see above it is defined. What is the problem?
How would you upload a video to twitter using the POST media/upload (chunked) endpoint with node?
This goes through all of the steps outlined in the link above: INIT, APPEND, FINALIZE and STATUS
var bufferLength, filePath, finished, fs, oauthCredentials, offset, request, segment_index, theBuffer;
request = require('request');
fs = require('fs');
filePath = '/thevideo.mp4';
bufferLength = 1000000;
theBuffer = new Buffer(bufferLength);
offset = 0;
segment_index = 0;
finished = 0;
oauthCredentials = {
consumer_key: '',
consumer_secret: '',
token: '',
token_secret: ''
};
fs.stat(filePath, function(err, stats) {
var formData, normalAppendCallback, options;
formData = {
command: "INIT",
media_type: 'video/mp4',
total_bytes: stats.size
};
options = {
url: 'https://upload.twitter.com/1.1/media/upload.json',
oauth: oauthCredentials,
formData: formData
};
normalAppendCallback = function(media_id) {
return function(err, response, body) {
finished++;
if (finished === segment_index) {
options.formData = {
command: 'FINALIZE',
media_id: media_id
};
request.post(options, function(err, response, body) {
console.log('FINALIZED',response.statusCode,body);
delete options.formData;
//Note: This is not working as expected yet.
options.qs = {
command: 'STATUS',
media_id: media_id
};
request.get(options, function(err, response, body) {
console.log('STATUS: ', response.statusCode, body);
});
});
}
};
};
request.post(options, function(err, response, body) {
var media_id;
media_id = JSON.parse(body).media_id_string;
fs.open(filePath, 'r', function(err, fd) {
var bytesRead, data;
while (offset < stats.size) {
bytesRead = fs.readSync(fd, theBuffer, 0, bufferLength, null);
data = bytesRead < bufferLength ? theBuffer.slice(0, bytesRead) : theBuffer;
options.formData = {
command: "APPEND",
media_id: media_id,
segment_index: segment_index,
media_data: data.toString('base64')
};
request.post(options, normalAppendCallback(media_id));
offset += bufferLength;
segment_index++
}
});
});
});
Please try this
const splitFile = require('split-file')
const Twitter = require('twitter')
const fs = require('fs-extra')
const Promise = require('bluebird')
const pathToMovie = __dirname + '/test/152.mp4';
const mediaType = 'video/mp4' // `'video/mp4'` is also supported
let Names
const mediaSize = require('fs').statSync(pathToMovie).size
/* Twitter support Maximum 15MB video files. So we need to split this
file in to three files */
splitFile.splitFile(pathToMovie, 3)
.then((names) => {
Names = names
return init()
})
.catch((err) => {
console.log('Error: ', err)
})
const client = new Twitter({
consumer_key: '<your consumer_key >',
consumer_secret: '<your consumer_secret >',
access_token_key: '<your access_token_key >',
access_token_secret: '<access_token_secret>'
});
const init = () => {
initTweetUpload(mediaSize, mediaType) // Declare that you wish to upload some media
.then(appendTweetUpload) // Send the data for the media
.then(appendTweetUpload) // Send the data for the media
.then(appendTweetUpload) // Send the data for the media
.then(finalizeTweetUpload) // Declare that you are done uploading chunks
// eslint-disable-next-line promise/always-return
.then((data) => {
const status = {
media_ids: data,
status: 'NodeJS Media Upload',
}
client.post('statuses/update', status, (error, tweet, response) => {
console.log(error)
console.log(tweet)
})
}).catch((err) => {
console.log('Error: ', err)
})
}
const initTweetUpload = (mediaSize, mediaType) => makePost('media/upload',
{
command: 'INIT',
total_bytes: mediaSize,
media_type: mediaType,
}).then((data) => data.media_id_string)
let i = 0
const appendTweetUpload = (mediaId) => {
const p = Names.shift()
/* mediaData is the raw binary file content being uploaded ,It must be
<= 5 MB */
const mediaData = fs.readFileSync(p)
return makePost('media/upload', {
command: 'APPEND',
media_id: mediaId,
media: mediaData,
segment_index: i++,
}).then((data) => mediaId)
}
const finalizeTweetUpload = (mediaId) => makePost('media/upload', {
command: 'FINALIZE',
media_id: mediaId,
}).then((data) => mediaId)
const makePost = (endpoint, params) =>
// params.media_category = 'tweet_video';
new Promise((resolve, reject) => {
client.post(endpoint, params, (error, data, response) => {
if (error) {
reject(error)
} else {
resolve(data)
}
})
})
dependencies
1. https://www.npmjs.com/package/twitter
2. https://www.npmjs.com/package/split-file