can't upload a image to express api with Multer from react - javascript

I want to upload a image from my react app to my express API.
i made something like this in vainla js and it work ed with same endpoint but in react project doesn't work .
I created a react component like this:
import React, { useState } from "react";
import BackupIcon from "#material-ui/icons/Backup";
import url from "../url";
import Preview from "./Preview";
const AdaugaImg = props => {
const [file, setFile] = useState(null);
const [ales, setAles] = useState(false);
const [imgUrl, setImgUrl] = useState("");
const modificare = e => {
//console.log(e.target.value);
setFile(e.target.files[0]);
setAles(true);
const urlImg = URL.createObjectURL(e.target.files[0]);
//console.log(urlImg);
//reader.onloadend = e => setImgUrl(reader.result);
setImgUrl(urlImg);
};
const upload = async e => {
e.preventDefault();
setAles(false);
console.log("incarcare");
console.log(file);
try {
const data = new FormData();
data.append("img", file);
const res = await fetch(`${url}/add`, {
method: "POST",
data: data
});
console.log(await res.json());
} catch (error) {
console.log(error);
}
};
const closePreview = () => setAles(false);
const preview = ales ? (
<Preview imgUrl={imgUrl} closePreview={closePreview} />
) : null;
return (
<div className="upload">
{preview}
<form method="POST" onSubmit={upload}>
<input
type="file"
name="img"
id="poza"
style={{ width: 0.1, height: 0.1 }}
onChange={modificare}
/>
{!ales ? (
<label for="poza" className="upload-label">
<p>Selecteaza o poza</p>
</label>
) : null}
<button className="upload-btn" type="submit">
<BackupIcon />
<p>Incarca poza</p>
</button>
</form>
</div>
);
};
export default AdaugaImg;
them I wrote code. for api using multer
const router = require("express").Router();
const multer = require("multer");
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, "img-biserici/");
},
filename: function(req, file, cb) {
cb(null, file.fieldname + "_" + Date.now() + "_.jpg");
}
});
const upload = multer({
storage: storage
});
router.post("/add", upload.array("img", 1), (req, res) => {
console.log(req.files[0]);
res.json({ ok: true });
});
when I upload image in console it shows me this error:TypeError: Cannot read property '0' of undefined .what I missed?and how to fix this?

You are missing content type in headers.
const res = await fetch(`${url}/add`, {
method: "POST",
data: data,
headers: {
'Content-Type': 'multipart/form-data'
}
});

Related

PDF along with text not getting uploaded to Express from React

I want to send book details along with the pdf of the book to the database.
This is a portion of my react code.
const {user}=useSelector((state)=>state.auth)
const [file, setFile] = useState(null);
const dispatch = useDispatch()
const { values, errors, touched, handleBlur, handleChange, handleSubmit, setFieldValue } = useFormik({
initialValues: initialValues,
validationSchema: addBookSchema,
onSubmit: async(values, { resetForm }) => {
// i have used FormData only to send pdf
const formData = new FormData()
formData.append('title', values.title)
formData.append('author', values.author)
formData.append('rating', values.rating)
formData.append('numOfRatings', values.numOfRatings)
formData.append('total', values.total)
formData.append('genre', values.genre)
formData.append('file', file)
const response = await axios.post('http://localhost:5000/api/admin', formData, {
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${user.token}`,
},
});
console.log(response.data);
resetForm()
}
})
return (
// some code
<Box>
<TextField
id="pdf-input"
// label="Upload PDF"
type="file"
accept=".pdf"
// className={classes.textField}
onChange={handleFileChange}
margin="normal"
/>
</Box>
// some code
Below is my express controller which handles this input from react
// this is to start connection
const url = process.env.MONGO_URI;
const connect = mongoose.createConnection(url, { useNewUrlParser: true, useUnifiedTopology: true });
let gfs;
connect.once('open', () => {
// initialize stream
gfs = new mongoose.mongo.GridFSBucket(connect.db, {
bucketName: "uploads"
});
});
// this is the controller
const addBook = asyncHandler(upload.single('file'),async (req, res) => {
console.log(req.file.file);
console.log(req.body);
// nothing gets logged in console
res.status(200).json(req.body)
});
below is my upload middleware used in my controller
const path = require("path");
const multer = require("multer");
const { GridFsStorage } = require("multer-gridfs-storage");
const crypto = require("crypto");
const storage = new GridFsStorage({
url: process.env.MONGO_URI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "uploads",
};
resolve(fileInfo);
});
});
},
});
const upload = multer({ storage });
module.exports = upload;
i get the following errors
//1. if i do formData.append('file', file) then i get "Warning: An unhandled error was caught from submitForm()"
//2. if i do formData.append('ebook', file) then i get "message: 'Unexpected field', stack: 'MulterError: Unexpected field\n"
//3. Nothing gets logged in my console when i console.log(req.body) in my controller.
Note: I am able to successfully send data to my database if i do not use FormData and do not take file input.
I tried various methods but i could not identify the problem. Any helps is very much appreciated. Thanks in advance.

FormData sent with React.js becomes empty when going to Express.js backend

I was creating a book library management CRUD and I am Using React v18.
I am struggling to add data to my mongdb atlas because whenever I pass formData to axios.post via Redux, I'm always getting empty req.body on the backend controller.
This however does not occur when I am using postman when adding data to my database via form-data. My login form which also using axios.post works fine and the booklist using axios.get also works fine, the crud for book is the only one that is not working.
This is my bookCreate.js:
const BookCreate = () => {
const [title, setTitle] = useState('')
const [responsibility, setResponsibility] = useState('')
const [uniform_title, setUniform_title] = useState('')
const [parallel_title, setParallel_title] = useState('')
const [main_author, setMain_author] = useState('')
const [other_author, setOther_author] = useState('')
const [contributors, setContributors] = useState('')
const [corp_author, setCorp_author] = useState('')
const [placePub, setPlacePub] = useState('')
const [publisher, setPublisher] = useState('')
const [yearPub, setYearPub] = useState('')
...
const submitHandler = (e) => {
e.preventDefault();
const formData = new FormData();
formData.set('title', title);
formData.set('responsibility', responsibility);
formData.set('uniform_title', uniform_title);
formData.set('parallel_title', parallel_title);
formData.set('main_author', main_author);
formData.set('other_author', other_author);
formData.set('contributors', contributors);
formData.set('corp_author', corp_author);
formData.set('placePub', placePub);
formData.set('publisher', publisher);
formData.set('yearPub', yearPub);
dispatch(newBooks(formData))
}
return (
<Fragment>
<MetaData title={'TUP-T Online Library - Admin'} />
{/*<div className="row">*/}
<SideNavbarAdmin/>
<div></div>
<div className="dashboard-content">
<div className="dashboard-page">
<div className="dashboard-header">
<h1>Add Book</h1>
</div>
<div className="dashboard-body">
<form className="" onSubmit={submitHandler} encType='multipart/form-data'>
<div className="row g-3">
<div className="col md-6">
<div className="form-group">
<label htmlFor="title_field">Title</label>
<input
type="text"
id="title_field"
className="form-control"
name='title'
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
...
}
This is bookAction.js:
export const newBooks = (bookData) => async (dispatch) => {
try {
dispatch({ type: NEW_BOOK_REQUEST })
const config = {
headers: {
"Content-Type": "multipart/form-data"
// "Content-Type": "application/json"
}
}
// for(var pair of bookData.entries()) {
// console.log(pair[0]+ ', '+ pair[1]);
// }
const { data } = await axios.post('/api/v1/book/new', bookData, config)
dispatch({
type: NEW_BOOK_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: NEW_BOOK_FAIL,
payload: error.response.data.message
})
}
}
This is bookController.js:
exports.createBook = async (req, res, next) => {
console.log(req.body);
}
My app.js on the backend:
const express = require("express");
const app = express();
const cookieParser = require("cookie-parser");
const errorMiddleware = require("./middlewares/errors");
app.use(express.json());
app.use(cookieParser());
app.use(errorMiddleware);
app.use(express.urlencoded({ extended: true }));
const book = require("./routes/book");
const auth = require("./routes/auth");
app.use("/api/v1", book);
app.use("/api/v1", auth);
app.use(errorMiddleware);
module.exports = app;
FormData send multipart/form-data, hence the parsers you used in app.js won't work. The common and easy way is to use multer. First install it:
npm install --save multer
Then where you hade urlencoded, and cookieParser use this:
const multer = require('multer');
const upload = multer();
app.use(upload.none())
I'm using this upload.none() because you seem to be not sending any file. If you are, you can visit the above link, mutter will let you do that.
Also you can simplify submitHandler since each form input has a name by getting ride of all those states and simply give the form to FormData() as parameter with e.target:
const submitHandler = (e) => {
e.preventDefault();
const formData = new FormData(e.target);
dispatch(newBooks(formData));
};

NodeJS-Reactjs File Upload Concurrent requests won't work

I am trying to build a project where Applicants can upload their CV and Images, These images and Resumes will be saved locally on the server, i am making two input components to select a file with a single upload button to make two axios requests to the nodejs server side to save the files in the required destinations when i make requests to upload both files only the first one will be saved while the other will be neglected
My Client side code for this part
import React from 'react';
import axios from 'axios';
import { useState, useEffect } from "react";
function FileUploads(props) {
const [file, setFile] = useState();
const [fileName, setFileName] = useState("");
const [image, setImage] = useState();
const [imageName, setImageName] = useState("");
const saveFile = (e) => {
setFile(e.target.files[0]);
setFileName(e.target.files[0].name);
console.log(e.target.files)
};
const saveImage = (e) => {
setImage(e.target.files[0]);
setImageName(e.target.files[0].name);
console.log(e.target.files)
};
const uploadFile = async (e) => {
console.log(file)
console.log(fileName)
console.log(image)
console.log(imageName)
const formData = new FormData();
formData.append("file", file);
formData.append("fileName", fileName);
const imageData = new FormData();
imageData.append("file", image);
imageData.append("fileName", imageName);
try {
const img = await axios.post(
"http://localhost:5000/uploadImage",imageData);
} catch (ex) {
console.log(ex);
}
try {
const img = await axios.post(
"http://localhost:5000/uploadCV",formData);
} catch (ex) {
console.log(ex);
}
};
return (
<body className="body">
<div className="containerMinimized" >
<div class="content">
<h2>Kindly Upload Your CV & Image</h2>
<h4 className='input-cv-text'>Upload your CV</h4>
<input className='input-cv' type="file" onChange={saveFile}/>
<h4 className='input-cv-text2'>Upload your Image</h4>
<input className='input-cv' type="file" onChange={saveImage}/>
<button className="checkButtonInput" onClick={uploadFile}>Upload</button>
</div>
</div>
</body>
);
}
export default FileUploads;
And here's my Endpoint using NodeJS
app.post("/uploadCV", (req, res) => {
const newpath = __dirname + "/applicantsResume/";
const file = req.files.file;
const filename = file.name;
console.log('')
file.mv(`${newpath}${filename}`, (err) => {
if (err) {
// res.status(500).send({ message: "File upload failed", code: 200 });
console.log(err)
}
// res.status(200).send({ message: "File Uploaded", code: 200 });
console.log('CV Uploaded Correctly')
});
});
app.post("/uploadImage", (req, res) => {
const newpath = __dirname + "/applicantsImages/";
const file = req.files.file;
const filename = file.name;
console.log('')
file.mv(`${newpath}${filename}`, (err) => {
if (err) {
// res.status(500).send({ message: "File upload failed", code: 200 });
console.log(err)
}
// res.status(200).send({ message: "File Uploaded", code: 200 });
console.log('Image Uploaded Succesfully')
});
});
I found a pattern sometimes when i restart the server it automatically inserts the missing file as if it was a pending request
Any ideas?

How to update files using Axios and React

I've built an api with posts/articles, and now I'm working on the front end. I'm having an issue with uploading a new photo for an already existing post. I've attempted the two most common methods for the photo, formData and JSON Base64. I'm receiving the same error response for both methods, Please upload a file, code: 400. I'm not certain where I've gone wrong, so I'll provide every relevant file.
articles.js(server)
exports.articlePhotoUpload = asyncHandler(async (req, res, next) => {
const article = await Article.findById(req.params.id);
if (!article) {
return next(
new ErrorResponse(`Article not found with id of ${ req.params.id }`, 404)
);
}
//Make sure user is article owner || make sure user is admin
if (article.user.toString() !== req.user.id || req.user.role !== 'admin') {
return next(
new ErrorResponse(
`User ${ req.user.id } is not authorized to update this article`,
)
)
}
if (!req.files) {
return next(new ErrorResponse(`Please upload a file`, 400))
}
const file = req.files.file;
// Confirm the image is a photo
if (!file.mimetype.startsWith('image')) {
return next(new ErrorResponse(`Please upload an image file`, 400))
}
// Check File Size
if (file.size > process.env.MAX_FILE_UPLOAD) {
return next(
new ErrorResponse(`Please upload an image less than ${ MAX_FILE_UPLOAD }`, 400)
)
}
// Create custom filename
file.name = `photo_${ article._id }${ path.parse(file.name).ext }`;
file.mv(`${ process.env.FILE_UPLOAD_PATH }/${ file.name }`, async err => {
if (err) {
console.log(err);
new ErrorResponse(`Problem with file upload`, 500);
}
await Article.findByIdAndUpdate(req.params.id, { photo: file.name });
res.status(200).json({
success: true,
data: file.name
});
});
});
^the above function is where the error message is triggered server-side.
axios.js(client-side)
...
axios.defaults.baseURL = 'http://<<ip address>>/api/v1';
axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('auth');
axios.defaults.headers.post['Content-Type'] = 'application/json';
export const uploadPhoto = (id, data) => {
return axios.put(`/articles/${ id }/photo`, {
data
})
}
...
^I have a feeling I could format this exported function better. I might be missing something here.
UploadPhoto.jsx(JSON Attempt)
const UploadPhoto = (props) => {
const { register, handleSubmit } = useForm();
const { state } = useStateMachine(updateAction);
const [file, setFile] = useState(null);
const [filename, setFilename] = useState(null);
const onSubmit = async () => {
const toBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
console.log(reader.result)
});
const data = {
title: filename,
file: await toBase64(file),
}
uploadPhoto(`5ea5e73044718d0b2c2ae5df`, data)
.then(res => console.log(res))
.catch(err => console.log(err.response.data.error))
}
const handleChange = (e) => {
setFile(e.target.files[0]);
setFilename(e.target.files[0].name);
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<h2>Cover Image</h2>
<label>
Select A File:
</label>
<img id="image" src="" alt="" />
<input
type="file"
name="photo"
ref={register}
defaultValue={state.data.photo}
onChange={handleChange}
/>
<label htmlFor="">
Filename
</label>
<input type="submit" />
</form>
)
}
export default withRouter(UploadPhoto);
UploadPhoto.jsx(formData attempt)
const onSubmit = () => {
const formData = new FormData();
formData.append("file", file);
console.log(file)
uploadPhoto(`5ea5e73044718d0b2c2ae5df`, formData)
.then(res => console.log(res))
.catch(err => console.log(err.response.data.error))
}
This is my first time building my own api, which is why I put the server code in here. However, I'm 90% sure I'm making a mistake client-side, most likely in the axios.js file.
Let's try this example:
const fileUpload = require('express-fileupload');
const app = express();
// enable files upload -- this should also make 'req.files' accessible
app.use(fileUpload({
createParentPath: true
}));
Add this in the beginning of your server initialization. After that your normal code should try accessing req.files as it should be filled on file upload requests now.
You will need to install module express-fileupload before using it in code -> npm i express-fileupload
(Source: https://attacomsian.com/blog/uploading-files-nodejs-express)

react js handling file upload

I'm new to react js. I want to upload image asynchronously with react js
Suppose I have this code
var FormBox = React.createClass({
getInitialState: function () {
return {
photo: []
}
},
pressButton: function () {
var data = new FormData();
data.append("photo", this.state.photo);
// is this the correct way to get file data?
},
getPhoto: function (e) {
this.setState({
photo: e.target.files[0]
})
},
render: function () {
return (
<form action='.' enctype="multipart/form-data">
<input type='file' onChange={this.getPhoto}/>
<button onClick={this.pressButton}> Get it </button>
</form>
)
}
})
ReactDOM.render(<FormBox />, document.getElementById('root'))
Any answer will be appreciated!
You can make use of FileReader
var FormBox = React.createClass({
getInitialState: function () {
return {
file: '',
imagePreviewUrl: ''
}
},
pressButton: function () {
e.preventDefault();
// TODO: do something with -> this.state.file
console.log('handle uploading-', this.state.file);
},
getPhoto: function (e) {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () => {
this.setState({
file: file,
imagePreviewUrl: reader.result
});
}
reader.readAsDataURL(file);
},
render: function () {
let {imagePreviewUrl} = this.state;
let imagePreview = null;
if (imagePreviewUrl) {
imagePreview = (<img src={imagePreviewUrl} />);
} else {
imagePreview = (<div className="previewText">Please select an Image for Preview</div>);
}
return (
<div>
<form action='.' enctype="multipart/form-data">
<input type='file' onChange={this.getPhoto}/>
<button onClick={this.pressButton}> Get it </button>
</form>
<div className="imgPreview">
{imagePreview}
</div>
</div>
)
}
})
ReactDOM.render(<FormBox />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="root"></div>
If you are planning to upload files using node and express then you have to create both server and client. The server has the api and the client is going to use that for uploading the file with axios.
Server part
First, we want to put in four packages that are express, explicit-fileupload,cors and nodemon. Run the below command to install applications.
npm i express express-fileupload cors nodemon
Now open the fileupload folder in your favorite code editor and create a brand new document referred to as server.js.
// server.js
const express = require('express');
const fileUpload = require('express-fileupload');
const cors = require('cors')
const app = express();
// middle ware
app.use(express.static('public')); //to access the files in public folder
app.use(cors()); // it enables all cors requests
app.use(fileUpload());
// file upload api
app.post('/upload', (req, res) => {
if (!req.files) {
return res.status(500).send({ msg: "file is not found" })
}
// accessing the file
const myFile = req.files.file;
// mv() method places the file inside public directory
myFile.mv(`${__dirname}/public/${myFile.name}`, function (err) {
if (err) {
console.log(err)
return res.status(500).send({ msg: "Error occured" });
}
// returing the response with file path and name
return res.send({name: myFile.name, path: `/${myFile.name}`});
});
})
app.listen(4500, () => {
console.log('server is running at port 4500');
})
use node server.js to start the server running.
Client
Open the react app folder on your favorite code editor and create a brand new report known as fileupload.js within the src folder. Now upload the following code.
// fileupload.js
import React, { useRef, useState } from 'react';
import axios from 'axios';
function FileUpload() {
const [file, setFile] = useState(''); // storing the uploaded file
// storing the recived file from backend
const [data, getFile] = useState({ name: "", path: "" });
const [progress, setProgess] = useState(0); // progess bar
const el = useRef(); // accesing input element
const handleChange = (e) => {
setProgess(0)
const file = e.target.files[0]; // accesing file
console.log(file);
setFile(file); // storing file
}
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
axios.post('http://localhost:4500/upload', formData, {
onUploadProgress: (ProgressEvent) => {
let progress = Math.round(
ProgressEvent.loaded / ProgressEvent.total * 100) + '%';
setProgess(progress);
}
}).then(res => {
console.log(res);
getFile({ name: res.data.name,
path: 'http://localhost:4500' + res.data.path
})
}).catch(err => console.log(err))}
return (
<div>
<div className="file-upload">
<input type="file" ref={el} onChange={handleChange} />
<div className="progessBar" style={{ width: progress }}>
{progress}
</div>
<button onClick={uploadFile} className="upbutton">
Upload
</button>
<hr />
{/* displaying received video*/}
{data.path && <video src={data.path} autoPlay controls />}
</div>
</div>
);
}
export default FileUpload;
Now import the FileUpload component inside the App.js file.
// App.js
import React from 'react';
import FileUpload from './fileupload';
import './App.css';
function App() {
return (
<div className="App">
<FileUpload />
</div >
);
}
export default App;
Start the react app by running npm start.
For more: React File Upload Demo
import axios from 'axios';
var FormBox = React.createClass({
getInitialState: function () {
return {
photo: [],
name : '',
documents:[]
}
},
pressButton: function () {
var component = this
var data = new FormData();
data.append("photo", component.state.photo, component.state.name);
var request = axios.post('http://localhost:3000/document', data)
request.then(function(response){
// you need to send data from server in response
if(response.status == 200){
console.log('saved in db')
component.state.documents.push(response.data.documents)
// pushed document data in documents array
}
})
},
getPhoto: function () {
var uploadfile = document.getElementById(upload_doc).files[0]
this.setState({
photo: uploadfile, name : uploadfile.name
})
},
render: function () {
var documents = this.state.documents.map((doc)=>{
return <div>
<a href={doc.url}>{doc.name}</a>
<img src={doc.photo} />
</div>
})
// you can show your documents uploaded this way using map function
return (
<form action='.' enctype="multipart/form-data">
<input type='file' id='upload_doc' onChange={this.getPhoto}/>
<button onClick={this.pressButton}> Get it </button>
<span>{documents}</span>
// this way you can see uploaded documents
</form>
)
}
})
ReactDOM.render(<FormBox />, document.getElementById('root'))
One more easier way, by using using axios node module axios-fileupload
npm install --save axios-fileupload
const axiosFileupload = require('axios-fileupload');
axiosFileupload(url,file);
You can use React Dropzone Uploader, which gives you a dropzone that shows file previews (including image thumbnails) for dropped or chosen files, and also handles uploads for you.
import 'react-dropzone-uploader/dist/styles.css'
import Dropzone from 'react-dropzone-uploader'
const Uploader = () => {
return (
<Dropzone
getUploadParams={() => ({ url: 'https://httpbin.org/post' })} // specify upload params and url for your files
onChangeStatus={({ meta, file }, status) => { console.log(status, meta, file) }}
onSubmit={(files) => { console.log(files.map(f => f.meta)) }}
accept="image/*,audio/*,video/*"
/>
)
}
Uploads have progress indicators, and they can be cancelled or restarted. The UI is totally customizable, and the library has no dependencies.
Full disclosure: I wrote this library.
Use following module to select images.
https://www.npmjs.com/package/react-image-uploader
You can then upload image to server using xhr request. Following is the sample code.
var xhr = new XMLHttpRequest();
xhr.onload = function (e) {
//your success code goes here
}
var formData = new FormData();
xhr.open("POST", url, true);
formData.append('file', fileData);
xhr.send(formData);
You can use whatwg-fetch to make a post request. In your pressButton() function make the following changes -
import 'whatwg-fetch';
//....
pressButton: function (url) {
var data = new FormData();
data.append("photo", this.state.photo);
var options = {
body: data,
timeout: 3000 * 60 * 60
};
let callOptions = {
method : 'POST',
headers : {
'Content-Type': 'application/json'
},
body : JSON.stringify(options.body)
}
return fetch(url, callOptions).then(response => response.json());
}
Retrieve all the data in the form by using FormData then get all the inputs value and create a post request using axios with header 'content-type': 'multipart/form-data', that's how it works.
const CreateProfile = () => {
const handleFormSubmit = async (event) => {
event.preventDefault();
const form = new FormData(event.currentTarget);
const data = {
name: form.get('name'),
email: form.get('email'),
phone: form.get('phone'),
image: form.get('image'),
}
const response = await axios.post('https://example.com/api/create/profile',data,{ headers: {
'content-type': 'multipart/form-data'
}});
console.log(response);
}
return (
<div>
<form onSubmit={(event) => handleFormSubmit(event)} method="post">
<input type="text" name="name" required />
<input type="email" name="email" required />
<input type="tel" name="phone" required />
<input type="file" accept="image/jpg, image/png, image/webp" name="image" required />
<button type="submit">Submit</button>
</form>
</div>);
}

Categories