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?
Related
i am trying to make a component that take a pdf from input or an already uploaded one and then extract pages from it and uploaded again
when choosing a file from input (choosing file from my computer)
i am using this
const handleFileChange = async (event) => {
const file = event.target.files[0];
setFiles(event.target.files[0])
const fileName = event.target.files[0].name
setFileName(fileName);
const fileReader = new FileReader();
fileReader.onload = async () => {
const pdfBytes = new Uint8Array(fileReader.result);
const pdfDoc = await PDFDocument.load(pdfBytes);
setPdfDoc(pdfDoc);
setPdfBlob(pdfBytes)
};
fileReader.readAsArrayBuffer(file);
setShowPdf(true)
};
we get a pdfDoc and a Unit8Array
then i use the pdfDoc to get pages and extract a new pdf file....
this works fine
now when selecting a file that we already uploaded
i use this to ping the api to get the file
const handleGetFile = async (url) => {
const headers = {
Authorization: "Bearer " + (localStorage.getItem("token")),
Accept: 'application/pdf'
}
await axios.put(`${process.env.NEXT_PUBLIC_API_URL}getPdfFileBlob`, {
pdfUrl: `https://handle-pdf-photos-project-through-compleated-task.s3.amazonaws.com/${url}`
}, { responseType: 'arraybuffer', headers }).then((res) => {
const handlePdf = async () => {
const uint8Array = new Uint8Array(res.data);
const pdfBlob = new Blob([uint8Array], { type: 'application/pdf' });
setPdfBlob(uint8Array)
// setPdfDoc(pdfBlob) .....? how do i create a pdf doc from the unit8array
}
handlePdf()
}).catch((err) => {
console.log(err)
})
}
this the the end point i am pinging
app.put('/getPdfFileBlob',async function(req,res){
try {
console.log(req.body.pdfUrl)
const url =req.body.pdfUrl;
const fileName = 'file.pdf';
const file = fs.createWriteStream(fileName);
https.get(url, (response) => {
response.pipe(file);
file.on('finish', () => {
file.close();
// Serve the file as a response
const pdf = fs.readFileSync(fileName);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader( 'Content-Transfer-Encoding', 'Binary'
);
res.setHeader('Content-Disposition', 'inline; filename="' + fileName + '"');
res.send(pdf);
});
});
} catch (error) {
res.status(500).json({success:false,msg:"server side err"})
}
})
after getting this file here is what am trying to do
const handlePageSelection = (index) => {
setSelectedPages(prevSelectedPages => {
const newSelectedPages = [...prevSelectedPages];
const pageIndex = newSelectedPages.indexOf(index);
if (pageIndex === -1) {
newSelectedPages.push(index);
} else {
newSelectedPages.splice(pageIndex, 1);
}
return newSelectedPages;
});
};
const handleExtractPages = async () => {
for (let i = pdfDoc.getPageCount() - 1; i >= 0; i -= 1) {
if (!selectedPages.includes(i + 1)) {
pdfDoc.removePage(i);
}
}
await pdfDoc.save();
};
well in the first case where i upload the pdf file from local storage i get a pdfDoc
console of pdf Doc and pdfBlob
and when i select already existing file i can't find a way to transfer unit8array buffer to pdf doc
log of pdfBlob and no pdf doc
what i want is transform the pdfblob to pdfDcoument or get the pdf document from the array buffer so i can use getpages on it
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.
I'm trying to make a upload image feature in my website. I've worked on uploading a single image and it worked how do I change my code to make it upload multiple images at same time code below:
Server Side:
// img storage confing
var imgconfig = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, "./uploads");
},
filename: (req, file, callback) => {
callback(null, `image-${Date.now()}.${file.originalname}`)
}
});
// img filter
const isImage = (req, file, callback) => {
if (file.mimetype.startsWith("image")) {
callback(null, true)
} else {
callback(null, Error("only image is allowd"))
}
}
var upload = multer({
storage: imgconfig,
fileFilter: isImage
})
// register userdata
app.post("/insertImage", upload.single("photo"), (req, res) => {
const { filename } = req.file;
console.log(req.file)
});
Client Side:
const [file, setFile] = useState("");
const setimgfile = (e) => {
setFile(e.target.files[0])
}
const addUserData = async (e) => {
e.preventDefault();
var formData = new FormData();
formData.append("photo", file)
const config = {
headers: {
"Content-Type": "multipart/form-data"
}
}
const res = await Axios.post("/insertImage", formData, config);
if (res.data.status == 201) {
console.log("NO error")
} else {
console.log("error")
}
}
REACT JSX
Here is my input file and multiple is added here
<div style={{ padding: 15, backgroundColor: '#fff', marginTop: 15 }}>
<h4>Upload file:</h4>
<input type="file" name='photo' onChange={setimgfile} multiple/>
<button onClick={addUserData}>submit</button>
</div>
Server code
app.post("/insertImages", upload.array("photos"), (req, res) => {
console.log(req.files)
});
Note: When uploading multiple images, Multer will populate req.files, and not req.file.
Client code
const formData = new FormData();
selectedImages.forEach((image, index) => {
formData.append(`photos[${index}]`, image)
})
<input type="file" name="photos" multiple>
Push all your files in your formdata using a loop, and the same thing to create the received files in disk.
dont forget to use multiple
<input type="file" id="fileInput" multiple />
for more details check this example https://www.freecodecamp.org/news/formdata-explained/
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)
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'
}
});