jeff
jeff

Reputation: 109

Google Apps Script: MailApp.sendEmail() can't send anything but TEXT and HTML files, nothing else?

I’m trying to attach a file to an email I send in Google Apps Script with MailApp.sendEmail(). In the browser JavaScript I read in the file manually with this code because I’ve already processed the equivalent of the submit button in my HTML form, and it works:

  var file = document.getElementById('myfile');
  var fileInfo = [];
  if(file.files.length)        // if there is at least 1 file
  {
    if (file.files[0].size < maxEmailAttachmentSize)  // and its size is < 25M
    {
      var reader = new FileReader();
      reader.onload = function(e)
      {
        fileInfo[0] = e.target.result;
      };
      reader.readAsBinaryString(file.files[0]);
      fileInfo[1] = file.files[0].name;
      fileInfo[2] = file.files[0].type;
      fileInfo[3] = file.files[0].size;
    }
    console.log(fileInfo);   // here I see the full file and info. All looks correct.
  }

Then I send it up to the server.

  google.script.run.withSuccessHandler(emailSent).sendAnEmail(fileInfo);

On the server I pull out the fields and send the email like so:

  var fname    = fileInfo[1];
  var mimeType = fileInfo[2];
  var fblob    = Utilities.newBlob(fileInfo[0], mimeType, fname);
  // all looks right in the Logger at this point

  try {
    GmailApp.sendEmail(emaiRecipient, emailSubject, emailBody, 
      {
        name: 'Email Sender',  // email sender
        attachments: [fblob]
      }
    );
  catch …

This works fine when the file is a text file or HTML file but doesn’t when the file is anything else. The file is sent but it's empty and apparently corrupt. Can anyone see what’s wrong with this code? (It doesn’t work with MailApp.sendEmail() either.) I did see in another stackoverflow post that the document has to be saved once, but that is something I definitely don’t want to do. Isn’t there any other way? What am I missing?

Upvotes: 1

Views: 336

Answers (1)

Tanaike
Tanaike

Reputation: 201613

Modification points:

  • FileReader works with the asynchronous process. This has already been mentioned by Rubén's comment.
  • In the current stage, when the binary data is sent to Google Apps Script side, it seems that it is required to convert it to the string and byte array. This has already been mentioned by TheMaster's comment.
  • In order to use your Google Apps Script, in this case, I think that converting the file content to the byte array of int8Array might be suitable.
    • For this, I used readAsArrayBuffer instead of readAsBinaryString.

When above points are reflected to your script, it becomes as follows.

Modified script:

HTML&Javascript side:

// Added
function getFile(file) {
  return new Promise((resolve, reject) => {
    var reader = new FileReader();
    reader.onload = (e) => resolve([...new Int8Array(e.target.result)]);
    reader.onerror = (err) => reject(err);
    reader.readAsArrayBuffer(file);
  });
}

async function main() {  // <--- Modified
  var file = document.getElementById('myfile');
  var fileInfo = [];
  if(file.files.length) {
    if (file.files[0].size < maxEmailAttachmentSize) {
      fileInfo[0] = await getFile(file.files[0]);  // <--- Modified
      fileInfo[1] = file.files[0].name;
      fileInfo[2] = file.files[0].type;
      fileInfo[3] = file.files[0].size;
    }
    console.log(fileInfo);   // here I see the full file and info. All looks correct.
    google.script.run.withSuccessHandler(emailSent).sendAnEmail(fileInfo);
  }
}
  • Although I'm not sure about your whole script from your question, at the modified script, it supposes that main() is run. When main() is run, the file is converted to the byte array and put it to fileInfo[0].
  • At Google Apps Script side, from fileInfo, var fblob = Utilities.newBlob(fileInfo[0], mimeType, fname); has the correct blob for Google Apps Script.

Google Apps Script side:

In this modification, your Google Apps Script is not modified.

References:

Added:

This code looks good but we can't use it because we are running on the Rhino JavaScript engine, not V8. We don't have support for newer JavaScript syntax. Could you give us an example of how it's done with older syntax? Ref

From your above comment, I modified as follows.

Modified script:

HTML&Javascript side:

function main() {
  var file = document.getElementById('myfile');
  var fileInfo = [];
  if(file.files.length) {
    if (file.files[0].size < maxEmailAttachmentSize) {
      var reader = new FileReader();
      reader.onload = function(e) {
        var bytes = new Int8Array(e.target.result);
        var ar = [];
        for (var i = 0; i < bytes.length; i++) {
          ar.push(bytes[i]);
        }
        fileInfo[0] = ar;
        fileInfo[1] = file.files[0].name;
        fileInfo[2] = file.files[0].type;
        fileInfo[3] = file.files[0].size;
        console.log(fileInfo);   // here I see the full file and info. All looks correct.
        google.script.run.withSuccessHandler(emailSent).sendAnEmail(fileInfo);
      }
      reader.onerror = function(err) {
        reject(err);
      }
      reader.readAsArrayBuffer(file.files[0]);
    }
  }
}

Google Apps Script side:

In this modification, your Google Apps Script is not modified.

Upvotes: 3

Related Questions