如何从firebase存储发送附加文件,并使用node.js中的mailgun服务将其发送到电子邮件

时间:2018-05-06 08:29:14

标签: node.js firebase google-cloud-storage firebase-storage mailgun

是否可以从firebase存储中附加文件? 我尝试了以下代码,但它不起作用

var mailgun = require("mailgun-js");
var api_key = 'key-acf9f881e32c85b3c0dad34358507a95';
var DOMAIN = 'sandbox76c6f74ddab14862816390c16f37a272.mailgun.org';
var mailgun = require('mailgun-js')({apiKey: api_key, domain: DOMAIN});
var path = require("path");

var filepath = path.join(`gs://i-m-here-c01f6.appspot.com/Groups/${leaderId}`, 'group_image.jpg');


var data = {
    from: 'Excited User <postmaster@sandbox76c6f74ddab14862816390c16f37a272.mailgun.org>',
    to: 'rayteamstudio@gmail.com',
    subject: 'Complex',
    text: 'Group Creation Request',
    html: `<p>A user named: ${fromName} wants to create a group.<br />
            User ID: ${leaderId}<br />
            Group Name: ${groupName}<br />
            Group Description: ${groupDescription}<br /><br />
            To Accept the request click here:<br />
            https://us-central1-i-m-here-c01f6.cloudfunctions.net/acceptOrDenyGroupCreation?leaderID=${leaderId}&requestStatus=approved <br /><br />
            To Deny the request click here:<br />
            https://us-central1-i-m-here-c01f6.cloudfunctions.net/acceptOrDenyGroupCreation?leaderID=${leaderId}&requestStatus=denied /></p>`,
    attachment: filepath
};

mailgun.messages().send(data, function (error, body) {
    if(error)
        console.log('email err: ',error);
});

请帮助

4 个答案:

答案 0 :(得分:1)

您无法使用404 page not found error网址从云存储中下载文件,就像它是HTTP网址一样。相反,你必须做其中一个:

  1. 使用Cloud Storage SDK在本地下载文件,然后将其附加到您的电子邮件
  2. 或者,使用Cloud Storage SDK生成“Signed URL”,它会为您提供该文件的HTTPS URL,可用于下载该文件。

答案 1 :(得分:1)

您可以将其作为缓冲区发送,如下所示:

var request = require('request');
var file = request("https://www.google.ca/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png");

var data = {
  from: 'Excited User <me@samples.mailgun.org>',
  to: 'serobnic@mail.ru',
  subject: 'Hello',
  text: 'Testing some Mailgun awesomeness!',
  attachment: file
};

mailgun.messages().send(data, function (error, body) {
  console.log(body);
});

来自here

答案 2 :(得分:0)

在您给出的示例中,您正在使用gs:://bucket-name/path从Cloud Storage下载文件并作为附件发送。为此,您需要根据文档使用request依赖项:

https://github.com/bojand/mailgun-js#attachments

在这种情况下,您要做的就是:

// var path = require("path");
const request = require('request');

var filepath = request(`gs://i-m-here-c01f6.appspot.com/Groups/${leaderId}`);

如果您想更详细地说明如何为文件分配属性,可以使用new mailgun.Attachments(options)来传递看起来像这样的options参数:

options: {
  data: filepath,
  filename: 'name,jpg',
  contentType: 'image/jpeg',
  knownLength: 2019121,
};

哪里

  • 数据-可以是以下之一:
    • 代表附件文件路径的字符串
    • 文件数据缓冲区
    • Stream的实例,这意味着它是可读的流。
  • filename-用于附件的文件名。默认为“文件”
  • contentType-内容类型。对于流数据的情况是必需的。例如图片/ jpeg。
  • knownLength-内容长度(以字节为单位)。对于Stream数据是必需的。

答案 3 :(得分:0)

[这是 Nodemailer 的一个例子,我花了很多时间从 Cloud Storage for Firebase 动态附加附件。因此,希望它会帮助某人。你会发现很多相似之处。

所有示例都在 Angular / TypeScript 中。

它开始于 component.html 文件

<form
  #formDirective="ngForm"
  [formGroup]="contactForm"
  (ngSubmit)="onSubmit(contactForm.value, formDirective)"
>
  <mat-form-field>
    <ngx-mat-file-input
      (change)="uploadFile($event)"
      formControlName="fileUploader"
      multiple
      type="file"
    >
    </ngx-mat-file-input>
  </mat-form-field>
</form>

component.ts

import { AngularFirestore } from '@angular/fire/firestore';
import {
  AngularFireStorage,
  AngularFireStorageReference,
  AngularFireUploadTask,
} from '@angular/fire/storage';
import {
  FormBuilder,
  FormGroup,
  FormGroupDirective,
  Validators,
} from '@angular/forms';
import { throwError } from 'rxjs';
...

constructor(
  private angularFirestore: AngularFirestore,
  private angularFireStorage: AngularFireStorage,
  private formBuilder: FormBuilder
) {}

public contentType: string[] = [];
public downloadURL: string[] = [];
public fileName: string = '';
public maxFileSize = 20971520;

public contactForm: FormGroup = this.formBuilder.group({
  fileUploader: [
    '',
    Validators.compose([
      // Your validators rules...
    ]),
  ],
  ...
});
...

public onSubmit(form: any, formDirective: FormGroupDirective): void {
  form.contentType = this.contentType;
  form.fileUploader = this.downloadURL;
  form.fileName = this.fileName;

  this.angularFirestore
    .collection(String(process.env.FIRESTORE_COLLECTION_MESSAGES)) // Make sure the environmental variable is a string.
    .add(form)
    .then(() => {
      // Your logic, such as alert...
    .catch(() => {
      // Your error handling logic...  
    });
}

public uploadFile(event: any): void {
  // Iterate through all uploaded files.
  for (let i = 0; i < event.target.files.length; i++) {
    const file = event.target.files[i]; // Get each uploaded file.
    const fileName = file.name + '_' + Date.now(); // It makes sure files with the same name will be uploaded more than once and each of them will have unique ID, showing date (in milliseconds) of the upload.

    this.contentType = file.type;
    this.fileName = fileName;

    // Get file reference.
    const fileRef: AngularFireStorageReference = this.angularFireStorage.ref(
      fileName
    );

    // Create upload task.
    const task: AngularFireUploadTask = this.angularFireStorage.upload(
      fileName,
      file,
      file.type
    );

    // Upload file to Cloud Firestore.
    task
      .snapshotChanges()
      .pipe(
        finalize(() => {
          fileRef.getDownloadURL().subscribe((downloadURL: string) => {
            this.angularFirestore
              .collection(String(process.env.FIRESTORE_COLLECTION_FILES)) // Make sure the environmental variable is a string.
              .add({ downloadURL: downloadURL });
            this.downloadURL.push(downloadURL);
          });
        }),
        catchError((error: any) => {
          return throwError(error);
        })
      )
      .subscribe();
  }
}

这就是前端,现在终于到了我们后端的时候了。

import { DocumentSnapshot } from 'firebase-functions/lib/providers/firestore';
import { EventContext } from 'firebase-functions';

async function onCreateSendEmail(
  snap: DocumentSnapshot,
  _context: EventContext
) {
  try {
    const contactFormData = snap.data();

    // You can use those to debug your code...
    console.log('Submitted contact form: ', contactFormData);
    console.log('context: ', _context); // This log will be shown in Firebase Functions logs.

    const mailTransport: Mail = nodemailer.createTransport({
      // Make sure the environmental variables have proper typings.
      host: String(process.env.MAIL_HOST),
      port: Number(process.env.MAIL_PORT),
      auth: {
        user: String(process.env.MAIL_ACCOUNT),
        pass: String(process.env.MAIL_PASSWORD),
      },
      tls: {
        rejectUnauthorized: false, //! Fix ERROR "Hostname/IP doesn't match certificate's altnames".
      },
    });

    const mailOptions = {
      attachments: [
        {
          contentType: `${contactFormData!.contentType}`,
          filename: `${contactFormData!.fileName}`,
          path: `${contactFormData!.fileUploader}`,
        },
      ],
      ... // Your other mails options such as bcc, from, to, subject, html...
    };

    await mailTransport.sendMail(mailOptions);
  } catch (err) {
    console.error(err);
  }
}

这或多或少是开发人员必须经历的从前端到后端的所有头痛问题,才能使用正确的 contentTypedownloadURLfileName 将文件动态附加到电子邮件中。我错过了整个 WWW 案例的完整解决方案,这就是从前到后回答的原因。

注意: 前端正在处理 Firebase 端的 UI/Cloud Storage 上的多个文件上传,并且所有文件都上传到 Firebase。但是,只有一个动态添加的附件可以正常工作。我仍然无法弄清楚如何处理多个动态文件上传。