title: Decoding base64 image email attachment retrieved using imap nodejs
body: <p>I'm attempting to retrieve an e-mail attachment image using Node.js imap that can be found here: <a href="https://github.com/mscdex/node-imap" rel="nofollow noreferrer">https://github.com/mscdex/node-imap</a></p>
  
  <p>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. </p>
  
  <p>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". </p>
  
  <p>Upon further investigation, if I convert it to a base64 string using this online tool: <a href="https://www.motobit.com/util/base64-decoder-encoder.asp" rel="nofollow noreferrer">https://www.motobit.com/util/base64-decoder-encoder.asp</a> and then go to a base64 to image converter (<a href="https://codebeautify.org/base64-to-image-converter" rel="nofollow noreferrer">https://codebeautify.org/base64-to-image-converter</a>), it shows the image just fine. </p>
  
  <p>I'm skeptical that my code is actually converting the image to base64 as the file size goes up from 250kb to 332kb. </p>
  
  <p>I am unsure of how to proceed to allow the photo to be properly decoded for viewing as the original .jpeg image.</p>
  
  <pre><code>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 &amp;&amp; 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 &lt; len; ++i) {
          if (Array.isArray(struct[i])) {
              findAttachmentParts(struct[i], attachments);
          } 
          else {
              if (struct[i].disposition &amp;&amp; ['INLINE', 'ATTACHMENT'].indexOf(struct[i].disposition.type) &gt; -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 &lt; 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();
  </code></pre>
  
  <p>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."</p>