PDF along with text not getting uploaded to Express from React - javascript

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.

Related

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?

Missing required parameter - file Cloudinary \Error

first time trying to upload images to Cloudinary and I have come across an issue Missing required parameter- file
In this way I'm Configuring my cloudinary
//Configuring Cloudinary
cloudinary.config({
cloud_name: process.env.ClOUDINARY_NAME,
api_key: process.env.CLOUDINARY_API_KEY ,
api_secret: process.env.CLOUDINARY_API_SECRET,
})
Im using this to add user avatar on cloudinary like this:
//Register user
exports.registerUser = catchAsyncError(async (req, res, next) => {
//avatar img upload
const myCloud = await cloudinary.v2.uploader.upload(req.body.avatar, {
folder: "avatars",
width: 150,
crop: "scale",
});
const { name, email, password } = req.body;
const user = await User.create({
name,
email,
password,
avatar: {
public_id: myCloud.public_id,
url: myCloud.secure_url,
},
});
This is how i am setting the FormData
const registerSubmit = (e) => {
e.preventDefault();
const myForm = new FormData();
myForm.set("name", name);
myForm.set("email", email);
myForm.set("password", password);
myForm.set("avatar", avatar);
dispatch(register(myForm));
};
//For Register
export const register = ({userData}) => async (dispatch) =>{
try{
dispatch({type: REGISTER_USER_REQUEST});
const config = { headers : {"Content-Type": "multipart/form-data"} };
const {data} = await axios.post(
`api/v1/register`,
{userData},
{config}
);
dispatch({type:REGISTER_USER_SUCCESS, payload: data.user});
}
catch(error){
dispatch({type:REGISTER_USER_FAIL, payload:error.response.data.message});
}
but after all this I'm still getting the same error, Please help me

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)

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

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'
}
});

Upload Image from form-data to S3 using a Lambda

So I am writing a Lambda that will take in some form data via a straight POST through API Gateway (testing using Postman for now) and then send that image to S3 for storage. Every time I run it, the image uploaded to S3 is corrupted and won't open properly. I have seen people having to decode/encode the incoming data but I feel like I have tried everything using Buffer.from. I am only looking to store either .png or .jpg. The below code does not reflect my attempts using Base64 encoding/decoding seeing they all failed. Here is what I have so far -
Sample Request in postman
{
image: (uploaded .jpg/.png),
metadata: {tag: 'iPhone'}
}
Lambda
const AWS = require('aws-sdk')
const multipart = require('aws-lambda-multipart-parser')
const s3 = new AWS.S3();
exports.handler = async (event) => {
const form = multipart.parse(event, false)
const s3_response = await upload_s3(form)
return {
statusCode: '200',
body: JSON.stringify({ data: data })
}
};
const upload_s3 = async (form) => {
const uniqueId = Math.random().toString(36).substr(2, 9);
const key = `${uniqueId}_${form.image.filename}`
const request = {
Bucket: 'bucket-name',
Key: key,
Body: form.image.content,
ContentType: form.image.contentType,
}
try {
const data = await s3.putObject(request).promise()
return data
} catch (e) {
console.log('Error uploading to S3: ', e)
return e
}
}
EDIT:
I am now atempting to save the image into the /tmp directory then use a read stream to upload to s3. Here is some code for that
s3 upload function
const AWS = require('aws-sdk')
const fs = require('fs')
const s3 = new AWS.S3()
module.exports = {
upload: (file) => {
return new Promise((resolve, reject) => {
const key = `${Date.now()}.${file.extension}`
const bodyStream = fs.createReadStream(file.path)
const params = {
Bucket: process.env.S3_BucketName,
Key: key,
Body: bodyStream,
ContentType: file.type
}
s3.upload(params, (err, data) => {
if (err) {
return reject(err)
}
return resolve(data)
}
)
})
}
}
form parser function
const busboy = require('busboy')
module.exports = {
parse: (req, temp) => {
const ctype = req.headers['Content-Type'] || req.headers['content-type']
let parsed_file = {}
return new Promise((resolve) => {
try {
const bb = new busboy({
headers: { 'content-type': ctype },
limits: {
fileSize: 31457280,
files: 1,
}
})
bb.on('file', function (fieldname, file, filename, encoding, mimetype) {
const stream = temp.createWriteStream()
const ext = filename.split('.')[1]
console.log('parser -- ext ', ext)
parsed_file = { name: filename, path: stream.path, f: file, type: mimetype, extension: ext }
file.pipe(stream)
}).on('finish', () => {
resolve(parsed_file)
}).on('error', err => {
console.err(err)
resolve({ err: 'Form data is invalid: parsing error' })
})
if (req.end) {
req.pipe(bb)
} else {
bb.write(req.body, req.isBase64Encoded ? 'base64' : 'binary')
}
return bb.end()
} catch (e) {
console.error(e)
return resolve({ err: 'Form data is invalid: parsing error' })
}
})
}
}
handler
const form_parser = require('./form-parser').parse
const s3_upload = require('./s3-upload').upload
const temp = require('temp')
exports.handler = async (event, context) => {
temp.track()
const parsed_file = await form_parser(event, temp)
console.log('index -- parsed form', parsed_file)
const result = await s3_upload(parsed_file)
console.log('index -- s3 result', result)
temp.cleanup()
return {
statusCode: '200',
body: JSON.stringify(result)
}
}
The above edited code is a combination of other code and a github repo I found that is trying to achieve the same results. Even with this solution the file is still corrupted
Figured out this issue. Code works perfectly fine - it was an issue with API Gateway. Need to go into the API Gateway settings and set thee Binary Media Type to multipart/form-data then re-deploy the API. Hope this helps someone else who is banging their head against the wall on figuring out sending images via form data to a lambda.

Categories