How to Upload and Send Images With Nodemailer

How to Send an Electronic mail with Image Attachment in Node.js & React

In the modern web, it becomes more and more than pop having a contact form with an image attachment feature. You might ask why practise I demand it? Well, what if your customer institute a bug and wants to ship a screenshot to you? It makes sense right? There are many tutorials on the cyberspace about sending emails with Node.js and Nodemailer but none of them covers the sending an zipper with it. Nosotros will get a footstep farther and build a contact form with epitome attachment using Node.js Limited with Nodemailer for dorsum-end and React with Redux for Forepart-cease. Permit's get started:

Setting up back-cease

Firstly we demand to gear up our projection:

          npm init        

And so I assume you lot are already familiar with generating projects with Node.js and React, so will not go further for basic topics. Nosotros need to install the Express framework, body-parser and other dependencies.

          npm install express torso-parser nodemailer cors        

Let's create our main server file chosen alphabetize.js:

          const express = crave('limited');          const bodyParser = crave('torso-parser');          const nodemailer = require('nodemailer');          const cors = crave('cors');          const app = express();          const port = 4444;          app.use(bodyParser.json({ limit: '10mb', extended: true }))          app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }))          app.apply(cors());          app.listen(port, () => {          console.log('We are live on port 4444');          });          app.become('/', (req, res) => {          res.send('Welcome to my api');          })        

It'due south a elementary Node.js API powered by Express. Later on creating information technology we need to accept the all benefit from Nodemailer and include the controller function that will transport the submitted e-mail with the image.

Creating contact controller part

We are going to use Gmail for authentication. Nevertheless, you tin utilize Sendgrid's API which offers a better and more than secure option. We are creating it in the Index.js since there is no any other functionality. We need just this simple controller and API road for sending post requests.

          app.postal service('/api/v1/contact', (req, res) => {          var data = req.torso;          var smtpTransport = nodemailer.createTransport({          service: 'Gmail',          port: 465,          auth: {          user: 'username',          laissez passer: 'password'          }          });          var mailOptions = {          from: information.email,          replyto: data.email,          to: 'goshareitio@gmail.com',          discipline: data.title,          html: `<p>${data.email}</p>          <p>${information.bulletin}</p>`,          attachments: [          {          filename: information.championship + ".jpg",          contentType:  'paradigm/jpeg',          content: new Buffer.from(req.body.image.divide("base64,")[ane], "base64"),          }          ]          };          smtpTransport.sendMail(mailOptions,          (error, response) => {          if (mistake) {          res.status(400).transport(error)          } else {          res.send('Success')          }          smtpTransport.shut();          });          })        

Inside the controller part, you will encounter the attachment field with supported file types. This is a built-in feature that comes with Nodemailer.

It's fourth dimension to motility on Front-stop.

Creating React.js project

We are going to use Create React App for generating our project.

          npx create-react-app Contact        

I guess you are familiar with react and have some knowledge so will not go farther for explaining project structure. While this tutorial covers a pocket-size part for a real-world project we are going to create reusable components because it'southward anticipated that you are going to use these reusable components not just for contact form just as well for other forms in the project like sign-in sign-up. posting, commenting and then on…

Creating reusable grade components

Firstly we will start with simple input component:

          import React from 'react';          export const ProjectInput = ({
input,
label,
type,
symbol,
className,
meta: { touched, error, warning }
}) => (
<div className='form-group'>
<label>{label}</label>
<div className='input-grouping'>
{symbol &&
<div className='input-group-prepend'>
<div className='input-group-text'>{symbol}</div>
</div>
}
<input {…input} blazon={type} className={className} />
</div>
{touched &&
((error && <div className='warning alert-danger'>{fault}</div>))}
</div>
)

Then will create TextArea

          import React from 'react';          export const ProjectTextArea = ({
input,
characterization,
type,
rows,
className,
meta: { touched, error, alarm }
}) => (
<div className='form-group'>
<label>{characterization}</label>
<div className='input-grouping'>
<textarea {…input} type={type} rows={rows} className={className}></textarea>
</div>
{touched &&
((fault && <div className='alert warning-danger'>{error}</div>))}
</div>
)

It's time to create the most of import reusable component of this form, an input for uploading the image.

Creating ImgFileUpload component

Nosotros are going to use FileReader. It is an object with the sole purpose of reading data from Hulk (and hence File likewise) objects.

It delivers the data using events, as reading from disk may accept time.

          import React from 'react';          export class ImgFileUpload extends React.Component {          constructor() {
super();
this.setupReader() this.land = {
selectedFile: undefined,
imageBase64: '',
initialImageBase64: '',
awaiting: imitation,
status: 'INIT',
}
this.onChange = this.onChange.bind(this);
}
setupReader() {
this.reader = new FileReader();
this.reader.addEventListener('load', (outcome) => {
const { initialImageBase64 } = this.state;
var { changedImage } = this.props;
const imageBase64 = event.target.result;
changedImage(imageBase64);
if (initialImageBase64) {
this.setState({ imageBase64 });
} else {
this.setState({ imageBase64, initialImageBase64: imageBase64 });
}
});
}
onChange(event) {
const selectedFile = consequence.target.files[0];
var { checkImageState } = this.props;
if (selectedFile) {
checkImageState('selected');
} else {
checkImageState('unselected');
}
if (selectedFile) {
this.setState({
selectedFile,
initialImageBase64: ''
});
this.reader.readAsDataURL(selectedFile);
}
}
render() { return (
<div className='img-upload-container'>
<label className='img-upload btn'>
<span className='upload-text'> Select an prototype </span>
<input type='file'
have='.jpg, .png, .jpeg'
onChange={this.onChange} />
</label>
</div>
)
}
}

There are many 3rd party libraries that you lot might discover for file selection and uploading, however, it'south recommended to ignore those libraries. Some of them cause unlike issues from which the most popular is that yous tin't create a production build because of a single third party library. We got a similar issue when used 3rd party package for Angular in the past. And so as fewer libraries you use every bit fewer issues will exist in the futurity.

Building a consummate contact form

We demand to put all the higher up-created components in one place for getting our terminal form simply before that, we need to install some dependencies.

npm install --save redux react-redux redux-grade axios

and then we need to create an index.js file that volition comprise all our reducers likewise every bit our formReducer

          import { createStore, compose, combineReducers } from 'redux';          import { reducer as formReducer } from 'redux-form';          export const init = () => {          const reducer = combineReducers({          form: formReducer,          });          const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;          const store = createStore(reducer);          return shop;          }        

Then don't forget to implement our shop inside App.js:

          import React, { Component } from 'react';          import { BrowserRouter, Route } from 'react-router-dom';          import { Provider } from 'react-redux';          import Contact from './components/contact';          import './App.css';          const store = require('./reducers').init();          course App extends Component {          render() {          return (          <Provider store={shop}>          <BrowserRouter>          <div className='App'>          <div className='container'>          <Route verbal path='/' component={Contact} />          </div>          </div>          </BrowserRouter>          </Provider>          );          }          }          export default App;        

Now, we can continue building our concluding contact form.

          import React from 'react';          import { Field, reduxForm } from 'redux-grade';          import { ProjectInput } from './ProjectInput';          import { ProjectTextArea } from './ProjectTextArea';          import { ImgFileUpload } from './ImgFileUpload';          class ContactForm extends React.Component {          state = {          imageState: false          }          render() {          const {          handleSubmit,          pristine,          submitting,          submitCb,          valid,          SetImage,          loading          } = this.props;          return (          <form onSubmit={handleSubmit(submitCb).demark(this)}          onClick={this.resetValues}>          <Field          proper name="email"          type="email"          label='Email'          className='form-control'          component={ProjectInput}          />          <Field          name="title"          type="text"          characterization='Title'          className='grade-control'          component={ProjectInput}          />          <Field          name="message"          type="text"          label='Description'          rows='6'          className='class-control'          component={ProjectTextArea}          />          <Field          name="paradigm"          characterization='Image'          className='class-control'          component={ImgFileUpload}          props={{          changedImage: (e) => {          SetImage(e);          this.setState({          imageState: truthful          })          },          checkImageState: (east) => {          if (e === 'selected') {          this.setState({          imageState: true          });          } else {          this.setState({          imageState: faux          });          }          },          }}          key={this.props.key}          />          {          loading ?          <button          className='btn btn-primary'          blazon="button"          disabled={true}          >          Sending...          </button>          :          <button          className='btn btn-primary'          type="submit"          disabled={!valid || pristine || submitting || !this.country.imageState}          >          Ship          </button>          }          </form>          )          }          }          const validate = values => {          const errors = {};          if (!values.electronic mail) {          errors.email = 'Delight enter email!';          }          if (!values.championship) {          errors.title = 'Please enter title!';          }          if (!values.bulletin) {          errors.bulletin = 'Please enter message!';          }          render errors;          }          consign default reduxForm({          course: 'ContactForm',          validate          })(ContactForm)        

We used reusable components which created before and used redux-grade to power our form. Likewise, nosotros implemented some validations for this course however, since information technology'due south a completely different topic, I volition recommend adding even more validations for each field separately. Besides that, we disabled the send button until all fields will be filled. Looks lots of work washed right? Every bit the final pace for making our contact form completely functional, we are going to create a container component for it.

          import React from 'react';          import ContactForm from './ContactForm';          import { reset } from 'redux-form';          import axios from 'axios';          import { connect } from "react-redux";          import { ToastContainer, toast } from 'react-toastify';          course Contact extends React.Component {          constructor(props) {          super(props);          this.land = {          errors: [],          note: '',          loading: simulated          }          this.pristine = false;          this.Ship = this.Send.bind(this);          }          SetImage = async (image) => {          await this.setState({ image });          };          Send(userData) {          allow { image } = this.state;          userData = { ...userData, image };          this.setState({ loading: true });          this.sendEmail(userData).then(          submited => {          toast.success('Email sent successfully');          this.props.acceleration(reset('ContactForm'));          this.setState({ primal: 'cleared' })          this.setState({ note: 'Email sent successfully', loading: fake });          },          ).catch(errors => {          toast.error('Fault occured')          this.setState({ errors, loading: faux })          });          };          sendEmail = async emailData => {          console.log(emailData);          return axios.postal service('/api/v1/contact', emailData).then(          res => res.data,          err => Hope.reject(err.response.data.errors)          )          };          render() {          const { errors } = this.state;          return (          <section id='contact'>          <ToastContainer />          <div className='bwm-grade'>          <div className='row'>          <div className='col-dr.-five'>          <h1>Contact U.s.a.</h1>          <ContactForm          loading={this.land.loading}          submitCb={this.Send}          errors={errors}          SetImage={this.SetImage}          pristine={this.pristine}          key={this.state.key}          />          </div>          </div>          </div>          </department>          )          }          }          export default connect(null, zilch)(Contact);        

In the to a higher place component, we send data to the dorsum-end. If the email was sent successfully the class is getting reset automatically. Also, nosotros used a toaster library for showing an alert however yous can just ignore that library. As you meet we used Axios for doing a post asking and sending data to the back-cease. Every bit far as Redux-form supports only resetting basic fields we created the functionality of resetting file input from scratch by using a special central in the contact form and contact components.

That's it! Promise this tutorial was helpful.
You can download the full source code hither:
Github

lopezconswited.blogspot.com

Source: https://javascript.plainenglish.io/how-to-send-an-email-with-image-attachment-in-node-js-react-657479d49587

0 Response to "How to Upload and Send Images With Nodemailer"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel