Decoding base64 image email attachment retrieved using imap nodejs - javascript

I'm attempting to retrieve an e-mail attachment image using Node.js imap that can be found here: https://github.com/mscdex/node-imap
After retrieving the image I want to save it to file and then save the name in the MySQL database so I can retrieve it in the front end using EJS.
I've gotten as far as retrieving the e-mail attachment image and attempting to decode it and then save it. Unfortunately when opened from the folder it says: "It appears that we don't support this file format".
Upon further investigation, if I convert it to a base64 string using this online tool: https://www.motobit.com/util/base64-decoder-encoder.asp and then go to a base64 to image converter (https://codebeautify.org/base64-to-image-converter), it shows the image just fine.
I'm skeptical that my code is actually converting the image to base64 as the file size goes up from 250kb to 332kb.
I am unsure of how to proceed to allow the photo to be properly decoded for viewing as the original .jpeg image.
var fs = require('fs'), fileStream;
var {Base64Encode} = require('base64-stream');
const Imap = require('imap'),
inspect = require('util').inspect;
var imap = new Imap({
user: 'gmailaccount#gmail.com',
password: 'gmailaccount',
host: 'imap.gmail.com',
port: 993,
tls: true
});
/* To Uppercase function */
function toUpper(thing) { return thing && thing.toUpperCase ? thing.toUpperCase() : thing;}
/* function to find attachments in imap email */
function findAttachmentParts(struct, attachments) {
attachments = attachments || [];
for (var i = 0, len = struct.length, r; i < len; ++i) {
if (Array.isArray(struct[i])) {
findAttachmentParts(struct[i], attachments);
}
else {
if (struct[i].disposition && ['INLINE', 'ATTACHMENT'].indexOf(struct[i].disposition.type) > -1) {
attachments.push(struct[i]);
}
}
}
return attachments;
}
function buildAttMessageFunction(attachment) {
var filename = attachment.params.name;
var encoding = attachment.encoding;
return function (msg, seqno) {
var prefix = '(#' + seqno + ') ';
msg.on('body', function(stream, info) {
//Create a write stream so that we can stream the attachment to file;
console.log(prefix + 'Streaming this attachment to file', filename, info);
var writeStream = fs.createWriteStream(filename);
writeStream.on('finish', function() {
console.log(prefix + 'Done writing to file %s', filename);
});
//stream.pipe(writeStream); this would write base64 data to the file.
//so we decode during streaming using
if (toUpper(encoding) === 'BASE64') {
//the stream is base64 encoded, so here the stream is decode on the fly and piped to the write stream (file)
stream.pipe(new Base64Encode()).pipe(writeStream);
} else {
//here we have none or some other decoding streamed directly to the file which renders it useless probably
stream.pipe(writeStream);
}
});
msg.once('end', function() {
console.log(prefix + 'Finished attachment %s', filename);
});
};
}
function openInbox(cb){
imap.openBox('INBOX', true, cb);
}
/* Take all unseen emails, output to console and save them to a text file */
imap.once('ready', function(){
openInbox(function(err, box){
if (err) throw err;
imap.search([ 'UNSEEN' ], function(err, results) {
var messages = [];
if (err) throw err;
var f = imap.fetch(results, { id: 1, bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)', '1.1'], struct: true });
f.on('message', function(msg, seqno) {
var body = ''
, header = ''
, parsedMsg = {}
var prefix = '(#' + seqno + ') ';
msg.on('body', function(stream, info) {
var buffer = '', count = 0;
if(info.which === 'TEXT' || info.which === '1.1'){
stream.on('data', function(chunk) { body += chunk.toString('utf8') })
stream.once('end', function() { parsedMsg.body = body })
}
else{
stream.on('data', function(chunk) { header += chunk.toString('utf-8') })
stream.once('end', function() { parsedMsg.header = Imap.parseHeader(header) })
}
stream.pipe(fs.createWriteStream('msg-' + seqno + '-body.txt'));
});
msg.once('attributes', function(attrs) {
var attachments = findAttachmentParts(attrs.struct);
console.log(prefix + 'Has attachments: %d', attachments.length);
for(var i = 0, len = attachments.length; i < len; ++i){
var attachment = attachments[i];
/*This is how each attachment looks like {
partID: '2',
type: 'application',
subtype: 'octet-stream',
params: { name: 'file-name.ext' },
id: null,
description: null,
encoding: 'BASE64',
size: 44952,
md5: null,
disposition: { type: 'ATTACHMENT', params: { filename: 'file-name.ext' } },
language: null
}
*/
console.log(prefix + 'Fetching attachment %s', attachment.params.name);
var f = imap.fetch(attrs.uid , {
bodies: [attachment.partID],
struct: true
});
//build function to process attachment message
f.on('message', buildAttMessageFunction(attachment));
}
parsedMsg.attrs = attrs;
console.log(prefix + 'Attributes: %s', inspect(attrs, false, 8));
});
msg.once('end', function() {
console.log(prefix + 'Finished email');
messages.push( parsedMsg );
});
});
f.once('error', function(err) {
console.log('Fetch error: ' + err);
});
f.once('end', function() {
console.log('Done fetching all messages!');
for( i in messages ) {
console.log( i + ': ' + inspect( messages[i], false, 4 ) );
}
imap.end();
});
});
});
});
imap.once('error', function(err){
console.log(err);
});
imap.once('end', function(){
console.log('Connection ended');
});
imap.connect();
The expected output is a .jpeg image saved to the file directory that is able to be viewed. The actual output I am getting is an image file that when double clicked to view says: "It appears that we don't support this file format."

Unfortunately, I couldn't find a way to use node-imap to properly decode and retrieve email attachments. I ended up using imap-simple instead and was able to achieve the desired result.
I used imap-simple's example code block to retrieve attachments.
var imaps = require('imap-simple');
var config = {
imap: {
user: 'your#email.address',
password: 'yourpassword',
host: 'imap.gmail.com',
port: 993,
tls: true,
authTimeout: 3000
}
};
imaps.connect(config).then(function (connection) {
connection.openBox('INBOX').then(function () {
// Fetch emails from the last 24h
var delay = 24 * 3600 * 1000;
var yesterday = new Date();
yesterday.setTime(Date.now() - delay);
yesterday = yesterday.toISOString();
var searchCriteria = ['UNSEEN', ['SINCE', yesterday]];
var fetchOptions = { bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)'], struct: true };
// retrieve only the headers of the messages
return connection.search(searchCriteria, fetchOptions);
}).then(function (messages) {
var attachments = [];
messages.forEach(function (message) {
var parts = imaps.getParts(message.attributes.struct);
attachments = attachments.concat(parts.filter(function (part) {
return part.disposition && part.disposition.type.toUpperCase() === 'ATTACHMENT';
}).map(function (part) {
// retrieve the attachments only of the messages with attachments
return connection.getPartData(message, part)
.then(function (partData) {
return {
filename: part.disposition.params.filename,
data: partData
};
});
}));
});
return Promise.all(attachments);
}).then(function (attachments) {
console.log(attachments);
// =>
// [ { filename: 'cats.jpg', data: Buffer() },
// { filename: 'pay-stub.pdf', data: Buffer() } ]
});
});

On both instances of Base64Encode rename it to Base64Decode.

Related

pdf encryption and uploading aws S3 not working

In this code i am trying to create pdf file and encrypting, i am able to create pdf encryption, in this code i am creating one Tempfile in my local machine after that i am encrypting that file and in here 'qpdf --encrypt Decorpot test 40 -- ${tempFileName} --replace-input' i am just replacing the non encrypted file with encrypted file and its storing correctly as a encrypted file in local machine,but when i am going to upload in s3 aws only non encryted file is added. i tried so many methods..please help me.. ThankYou
module.exports.generateChecklistPdf2 = function () {
return new Promise((resolve, reject) => {
let htmlFilePath = '';
htmlFilePath = path.join(__dirname, '../static/html/checkList.html');
htmlFilePath = htmlFilePath.replace(new RegExp(/\\/g), '/');
pdfFilePath = path.join(__dirname, '../uploads/' + Math.ceil(Math.random() *
810000) + '.pdf');
pdfFilePath = pdfFilePath.replace(new RegExp(/\\/g), '/');
let date = new Date();
return ejs.renderFile(htmlFilePath, { moment: moment, date }, {}, function (err,
htmlFile) {
if (err) return console.log(err);
var tempFileName = pdfFilePath;
var file = fs.createWriteStream(tempFileName);
assetsPath = path.join(__dirname, '../static/');
assetsPath = assetsPath.replace(new RegExp(/\\/g), '/');
options = {
base: 'file:///' + assetsPath,
}
setTimeout(() => {
var cmd = `qpdf --encrypt Decorpot test 40 -- ${tempFileName}
--replace-input`;
exec(cmd, function (err) {
if (err) {
console.error('Error occured: ' + err);
} else {
console.log('PDF encrypted :)', tempFileName);
}
});
}, 1000)
return pdf.create(htmlFile, options).toStream(function (err, stream) {
stream.pipe(file).on('close', () => {
formData = {
customerName: "customername",
file: fs.createReadStream(pdfFilePath)
}
let url = '';
if (process.env.NODE_ENV == 'production') {
url = 'http://13.233.26.162:5003/api/file/upload';
} else {
url = 'http://localhost:5003/api/file/upload';
}
return request.post({ url: url, formData: formData },
function (err, res) {
if (err) return console.log(err);
console.log(res.body)
let resolveObject = {};
resolveObject.pdfUrl = res.body;
resolve(resolveObject);
});
});
});
});
});
}

Method sendVoice() Telegram bot - the file is empty

I have this JavaScript code:
bot.sendVoice(chatId, 'C:/Users/a.repetto/Desktop/repettobot/hello_world.ogg', {
disable_notification: true,
reply_to_message_id: msg.message_id
}).then(function () {
// reply sent!
});
The file exists and contain the audio, but when I try to run the entire code I get this error:
""error_code":400,"description":"Bad Request: File must be non-empty "
I have also tried with sendAudio but this also doesn't work.
Edit 1: To be more specific.
If I write a script with only 'sendAudio' or 'sendVoice', the file is sent correctly to Telegram. But if I use this blcok of script, the file results empty:
var Bot = require('node-telegram-bot-api');
var watson = require('watson-developer-cloud');
var request = require('request');
var fs = require('fs');
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
var speech_to_text = watson.speech_to_text({
username: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX, //username from VCAPSERVICES
password: 'XXXXXXXX', //password from VCAP SERVICES
version: 'v1',
url: 'https://stream.watsonplatform.net/speech-to-text/api'
});
var language_translation = watson.language_translator({
username: 'XXXXXXXXXXXXXXXXXX',
password: 'XXXXXXXX',
version: 'v2'
});
var text_to_speech = watson.text_to_speech({
username: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
password: 'XXXXXXXXXX',
version: 'v1'
});
var params = {
model: 'en-US_BroadbandModel', //you can change the language here
content_type: 'audio/ogg;codecs=opus',
continuous: true,
interim_results: false
};
var bot = new Bot('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', {polling: true }); //token generated by the bot you created
bot.on('message', function (msg) {
if(msg['voice']){ return onVoiceMessage(msg); }
});
function onVoiceMessage(msg) {
var chatId = msg.chat.id;
bot.getFileLink(msg.voice.file_id).then(function(link) {
var recognizeStream = speech_to_text.createRecognizeStream(params);
recognizeStream.setEncoding('utf8');
recognizeStream.on('results', function(data) {
var result = data.results[0].alternatives[0].transcript;
var c = result;
console.log("testo: " + result);
bot.sendMessage(chatId, result, {
disable_notification: true,
reply_to_message_id: msg.message_id
}).then(function(){})
language_translation.translate({
text: result,
source: 'en',
target: 'it'
}, function(err,translation) {
if (err) {
console.log(err);
}
else {
var trad = translation.translations[0].translation;
console.log("traduzione: " + trad + "\n")
}
bot.sendMessage(chatId, trad, {
disable_notification: true,
reply_to_message_id: msg.message_id
}).then(function(){})
var params1 = {
text: trad,
voice: 'it-IT_FrancescaVoice',
accept: 'audio/ogg;codecs=opus'
}
bot.sendVoice(chatId, text_to_speech.synthesize(params1), {
disable_notification: true,
reply_to_message_id: msg.message_id
}).then(function(promise){console.log(promise)})
})
})
request(link).pipe(recognizeStream);
})
}
Telegram cannot send file from your PC. First upload your files and then replace local address with web address. Other way is send file to telegram bot and get file_id (use getUpdate method) and put file_id in your code.

Downloading multiple files using AngularJS and storing in path

I am new to Angular,I have a scenario, where I need to download multiple files at the same time. My files are stored in GridFS. I am able to download the files, but for example a pdf is blank. The contentType stored in gridFS is "contentType": "binary/octet-stream", Am I missing out on anything?
My Jade code is
tr(ng-repeat='row in displayedCollection')
td {{ row.Name}}
td {{ row.email}}
td
button.btn.btn-info(type='button',ng-click="downloadDocuments(row.documentsSubmitted)" download) Download Documents
My controller code is
$scope.downloadDocuments = function (row) {
angular.forEach(row, function (value, key) {
var fileToDownload = value.id + "," + 'TrainingPartnerAddingTrainingCenter';
$http.get('/downloadDocuments/' + fileToDownload).success(function (result, status, headers, config) {
var _contentType = (headers('Content-Type'));
var _fileName = headers('FileName');
var blob = new Blob([ result ], { type : _contentType });
var url = (window.URL || window.webkitURL).createObjectURL(blob);
var anchor = angular.element('<a/>');
anchor.attr({
href : url,
target : '_blank',
download : _fileName
})[0].click();
});
});
};
my node.js code is as follows
exports.downloadDocument = function (req, res) {
var paramData = req.params.fileToDownload.split(',');
var role = req.session.user.role;
var conn = mongoose.connection;
var gfs = Grid(conn.db, mongoose.mongo);
routesLogger.logInfo(role, "downloadDocument", "START");
gfs.findOne({_id: paramData[0], root: paramData[1]}, function (err, file) {
if (err) {
routesLogger.logError(role, "downloadDocument", err);
return res.status(400).send(err);
}
else if (!file) {
routesLogger.logError(role, "downloadDocument", "No File Found for download");
return res.status(404).send('Error on the database looking for the file.');
}
else {
res.set('Content-Type', file.contentType);
res.set('Content-Disposition', 'attachment; filename="' + file.filename + '"');
var readstream = gfs.createReadStream({
_id: paramData[0],
root: paramData[1]
});
readstream.on("error", function (err) {
routesLogger.logError(role, "downloadDocument", err);
res.end();
});
readstream.pipe(res);
routesLogger.logInfo(role, "downloadDocument", "END");
}
});
};
So the mistake I was doing was not adding the parameter { responseType: 'arraybuffer' }. I found the answer in this link
AngularJS: Display blob (.pdf) in an angular app
$http.get('/downloadDocuments/' + fileToDownload,{ responseType: 'arraybuffer' }).success(function (result, status, headers, config) {
console.log(headers('Content-Type'));
var _contentType = (headers('Content-Type'));
var _fileName = headers('FileName');
var blob = new Blob([result], {type: _contentType });
saveAs(blob, _fileName);

Sequelize send picture from blob

Right now I create a new url based on rowid and stream the picture back using fs. Below is my code that works right now.
This saves the picture from the blob pulled from the database.
exports.picFileSave = function (name, data) {
try{
// Query the entry
stats = fs.lstatSync(name);
// Is it a directory?
if (stats.isFile()) {
//do nothing;
}
} catch (e) {
var wstream = fs.createWriteStream(name);
wstream.write(data);
wstream.end();
}
};
This function gets the picture filename and sends it back
exports.getLocBg = function (rowid, callback) {
var filename = "tmp/loc/" + rowid + ".png";
try{
// Query the entry
stats = fs.lstatSync(filename);
// Is it a directory?
if (stats.isFile()) {
callback(filename);
}
} catch (e) {
locationdb.location
.findOne({where: {id: rowid}})
.then(function (locations) {
var wstream = fs.createWriteStream(filename);
wstream.write(locations.locationBackground);
wstream.end();
callback(filename);
});
}
};
This is simply the route that connects the client to the server
app.get('/mobile/locations/locbg/:rowid', function (req, res) {
var options = {
root: "./",
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
var rowid = req.params.rowid;
location.getLocBg(rowid, function (callback) {
res.sendFile(callback, options, function (err) {
if (err) {
console.log(err);
res.status(err.status).end();
}
else {
console.log('Sent:', callback);
}
});
});
});
I want to be able to simply pull the blob from the database and send the picture back without writing a file and sending that back. How would I go about this?

angular js file upload using http post method

I am tring to upload a file using angular $http method to node backend
I want to upload the form with additional form fields.
This is my code
var data = new FormData();
data.append('title', 'sometitle');
data.append('uploadedFile', $scope.uploadedFile);
FileService.upload(url, data, function(data, status) {
if(status===HTTP_OK) {
$scope.uploadSuccess = true;
$scope.showUploadProgressBar = false;
} else {
// error occured
console.log(data);
}
});
FileService
FileService.upload = function(url, data, callback) {
$http({
method : 'POST',
url : url,
data : data,
headers: {'Content-Type': undefined },
transformRequest: angular.identity
}).success(function(data, status) {
callback(data, callback);
}).error(function(data, status) {
callback(data, status);
});
};
I am using node multiparty module for file upload. I am receiving the file correctly. But the field value for title is undefined.
I don't know why title value is undefined
Node.js backend file upload handler
var form;
if(options.uploads.tempDir) {
form = new multiparty.Form({uploadDir : options.root + '/' + options.uploads.tempDir});
} else {
form = new multiparty.Form();
}
form.on('file', function(name, receivedFile) {
var tmpPath = receivedFile.path,
fileName = receivedFile.originalFilename,
targetDirectory = uploadDirectory + '/' + req.params.id,
targetPath = targetDirectory + '/' + fileName,
file = {
filePath : targetPath,
tempPath : tmpPath,
fileName : fileName,
size : receivedFile.size
};
fileUploadStatus.file = file;
// move file
fse.move(tmpPath, targetPath, function(err) {
if(err) {
console.log('Error moving file [ ' + targetPath + ' ] ' + JSON.stringify(err));
}
});
});
form.on('error', function(err) {
fileUploadStatus.err = err;
req.fileUploadStatus = fileUploadStatus;
next();
});
form.on('close', function() {
req.fileUploadStatus = fileUploadStatus;
next();
});
form.on('field', function(name, value) {
console.log('field called');
console.log(name);
console.log(value);
req.body = req.body || {};
req.body[name] = value;
});
// ignoring parts. Implement any other logic here
form.on('part', function(part) {
var out = new stream.Writable();
out._write = function (chunk, encoding, done) {
done(); // Don't do anything with the data
};
part.pipe(out);
});
// parsing form
form.parse(req);

Categories