Uploading Image file to Mongodb creates an empty item. React JS - reactjs

I am currently developing a web app in React JS using a mongodb database to upload data. I am trying to upload image files to the database but the items show up empty. How would I fix this problem?
Backend code for uploading data
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
const url = "mongodb://localhost:27017/StudyAppDB";
const storage = new GridFsStorage({ url });
const upload = multer({ storage });
const app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
mongoose.connect("mongodb://localhost:27017/StudyAppDB", {useNewUrlParser: true, useUnifiedTopology: true});
const imageSchema = {
key: String,
file: {
name: String,
lastModified: Number,
lastModifiedDate: String,
size: Number,
type: String,
webkitRelativePath: String,
},
filename: String,
}
const Images = mongoose.model("Images", imageSchema);
app.post('/uploadfile', upload.single('avatar'), (req, res) => {
const newImages = new Images(
req.body.data
)
newImages.save(function (err) {
if (!err) {
res.send("saved");
} else {
res.send(err);
}
});
})
app.listen(5000, function (req, res) {
console.log("listening");
})
Frontend code for collecting data:
fileUploadHandler is the function that uploads data to the database.
import React, {useState} from 'react';
import './Feed.css';
import axios from 'axios';
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
const url = "mongodb://localhost:27017/StudyAppDB";
// Create a storage object with a given configuration
const storage = new GridFsStorage({ url });
// Set multer storage engine to the newly created object
const upload = multer({ storage });
export default function PostForm(props) {
const [selectedFile, setSelectedFile] = useState(null)
function fileSelectedHandler(event) {
setSelectedFile(event.target.files[0])
}
function fileUploadHandler(){
// Create an object of formData
const formData = new FormData();
// Update the formData object
formData.append(
"myFile",
selectedFile,
selectedFile.name
);
axios.post("uploadfile", upload.single('avatar'), {data: formData})
.then(function (response) {
//after submitting go to calendar page or whatever
})
.catch(function (error) {
console.log(error);
});
}
return (
<div className="post-form">
<form onSubmit={handleSubmit} style={{width: '99%'}}>
Category:
<div>
<select style={{width: '15%', padding: '0.2%', marginTop: '1%'}} value={category} onChange={(e) => setCategory(e.target.value)}>
{props.categories.map((category, index) =>
<option key={category} value={category}>{category}</option>
)}
</select>
</div>
<br/>
<div style={{textAlign:'center'}}><textarea className='newContentText' value={content} onChange={e => setContent(e.target.value)}/> </div>
<input type="file" onChange={fileSelectedHandler}/>
<button onClick={fileUploadHandler}>Upload</button>
<button className="postButton">Post</button>
</form>
</div>
)
}

Related

Upload multiple images to separate folder in Next Js [duplicate]

I have a task for uploading images using Next js. API for image uploading has been given and it exists in the same project and when it becomes successful images are stored in a separate directory.
API for image uploading is as follows,
import formidable from "formidable";
import path from "path";
import { renameSync } from "fs";
export const config = {
api: {
bodyParser: false,
},
};
export default async function upload(req, res) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
const form = new formidable.IncomingForm({
keepExtensions: true,
maxFileSize: 200 * 1024 * 1024,
});
return new Promise((resolve, reject) => {
form.parse(req, async (err, fields, files) => {
if (err) {
res.status(500).json({ error: err.message });
return resolve();
}
if (!files.image) {
res.status(422).json({ error: "Bad request: missing image field" });
return resolve();
}
const ext = path.extname(files.image.path);
const uuid = Math.random().toString(26).slice(2);
await renameSync(files.image.path, `./public/images/${uuid}${ext}`);
res.status(201).json({
uuid,
url: `/images/${uuid}${ext}`,
});
return resolve();
});
});
}
Code for the front-end is mentioned below,
import React from "react";
export default function UploadImage() {
const [imgsSrc, setImgsSrc] = React.useState([]);
const uploadToClient = (e) => {
for (const file of e.target.files) {
setImgsSrc((imgs) => [...imgs, file]);
}
};
const uploadToServer = async () => {
let formData = new FormData();
for (const file of imgsSrc) {
formData.append('image', file, file.name)
}
const response = await fetch("/api/upload", {
method: "POST",
body: formData
});
};
return(
<div>
<h3>Upload photos</h3>
<input onChange={uploadToClient} type="file" name="file" multiple />
<div>
{imgsSrc.map((link) => (
<img src={link} style={{ width: 200, height: 200 }} />
))}
</div>
<button type="submit" onClick={uploadToServer}>Send to server</button>
</div>
)
}
When uploading images I am getting 'error - unhandledRejection: TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined'.
Any suggestion would be highly appreciated!

How can we receive a csv file in server side react hooks web app

In react hooks web app, how can we receive a csv file in the server side. The below is not working as I am getting the file undefined in server side. Could someone please advise ?
server.js
const multer = require('multer');
const bodyParser = require("body-parser");
const path = require('path');
app.use(express.static(path.join(__dirname, 'public')));
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/csv')
},
filename: function (req, file, cb) {
var ext = file.originalname.split('.').pop();
cb(null, file.fieldname + '-' + Date.now() + '.' + ext);
}
})
var upload = multer({ storage: storage });
app.put('/service/managenominees', upload.single('file'), async (req, res, next) => {
// csv file has two columns named Name, Email, I would like to receive value from those..
const data = req.file;
try {
if(req.body.file){
var name = req.file.Name;
var email = req.file.Email;
}
var nomineeData = {userName: name, userEmail: email};
res.status(200).send(nomineeData);
} catch (e) {
res.status(500).json({ fail: e.message });
}
});
manageNominee.js
import React, { useRef, useEffect, useState } from "react";
import Axios from "axios";
const ManageNominees = () => {
const [uploadFile, setUploadFile] = React.useState();
const [csvData, setCsvData] = useState([]);
const onChangeCsv = (e) => {
setCsvData(e.target.files[0]);
}
const submitForm = (data) => {
const dataArray = new FormData();
dataArray.append("uploadFile", data);
Axios.put("http://localhost:8000/service/managenominees", dataArray, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then((response) => {
// successfully uploaded response
})
.catch((error) => {
// error response
});
};
return (
<div>
<form onSubmit={submitForm} encType="multipart/form-data">
<h1>Upload Data</h1>
<input type="file" name="csvfile" onChange={onChangeCsv}/>
<button>Submit</button>
</form>
</div>
)
}
export default ManageNominees
There are two issues:
HTML attribute and the mutler upload option are different.
File values cant be accessed directly either convert the buffer and read the content or read the file (below code reads the file).
const multer = require('multer');
const bodyParser = require("body-parser");
const path = require('path');
const csv = require('csv-parser');
const fs = require('fs');
...
app.put('/service/managenominees', upload.single('csvfile'), (req, res, next) => {
console.log(req.file);
fs.createReadStream(req.file.path)
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', () => {
console.log(results);
// Result would be array as its CSV, iterate over the array and to get username and email id.
res.status(200).send(results);
});
});
Note: Code does not handle if the file does not exist.

How to upload files to AWS S3 bucket from the react frontend

Hello I am trying to upload images from my react frontend but its unable to read the file in axios and the images are not being uploaded.
This is my code.
<form onSubmit={Upload}>
<input
type='file'
onChange={(e) => {
generate(e.target.files);
}}
/>
<button type='submit'>Submit</button>
</form>
const generate = async (data) => {
const payload = {
entity: 'test',
userId: '200',
documentFileName: data[0].name,
action: 'putObject',
};
console.log('DAATA', data[0].name);
await api
.generatePreSignedUrl(payload, token)
.then((res) => {
setUploadUrl(res.data.data);
setImageData(data);
})
.catch((err) => {
console.log('Error', err);
});
};
const Upload = async (e) => {
e.preventDefault();
console.log('IMAGEdata', imageData);
console.log('URL', uploadUrl);
let file = imageData[0];
console.log('THIS IS FILE', file);
console.log('FILENAME', file.name);
console.log('FIELETYPE', file.type);
var options = {
headers: {
'Content-Type': file.type,
},
};
await axios
.put(uploadUrl, file, options)
.then((res) => {
console.log('Response from s3');
console.log(res);
})
.catch((error) => {
alert('ERROR ' + JSON.stringify(error));
});
};
Here imageUrl is the signed URL that I get from was to upload my files. I am successfully able to call the put request to upload the file but the image or file is not being read and uploaded.
This is the response when I am trying to look at the uploaded file.
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
<Key>documents/ama/test/200/displaypicture.png</Key>
<RequestId>R7M63AHQKDNC6NH5</RequestId>
<HostId>3u8Jb8Ev4S1s/ODicL3Py4hvrvwKKOs/5zGxUEDYORfN1+U37Zx8PfYvIcGWUIAfeCqHpWqXd8o=</HostId>
</Error>
Look at the AWS SDK for JavaScript Developer Guide Version 3. There are examples that show how to upload photos to an Amazon S3 bucket.
Uploading photos to Amazon S3 from a browser
A pretty simple way to use this node module to achieve this:
https://www.npmjs.com/package/react-aws-s3
https://medium.com/#steven_creates/uploading-files-to-s3-using-react-js-hooks-react-aws-s3-c4c0684f38b3
enter image description here
npm install --save react-aws-s3
User " aws-sdk " and use this code
import React ,{useState} from 'react';
import AWS from 'aws-sdk'
const S3_BUCKET ='YOUR_BUCKET_NAME_HERE';
const REGION ='YOUR_DESIRED_REGION_HERE';
AWS.config.update({
accessKeyId: 'YOUR_ACCESS_KEY_HERE',
secretAccessKey: 'YOUR_SECRET_ACCESS_KEY_HERE'
})
const myBucket = new AWS.S3({
params: { Bucket: S3_BUCKET},
region: REGION,
})
const UploadImageToS3WithNativeSdk = () => {
const [progress , setProgress] = useState(0);
const [selectedFile, setSelectedFile] = useState(null);
const handleFileInput = (e) => {
setSelectedFile(e.target.files[0]);
}
const uploadFile = (file) => {
const params = {
ACL: 'public-read',
Body: file,
Bucket: S3_BUCKET,
Key: file.name
};
myBucket.putObject(params)
.on('httpUploadProgress', (evt) => {
setProgress(Math.round((evt.loaded / evt.total) * 100))
})
.send((err) => {
if (err) console.log(err)
})
}
return <div>
<div>Native SDK File Upload Progress is {progress}%</div>
<input type="file" onChange={handleFileInput}/>
<button onClick={() => uploadFile(selectedFile)}> Upload to S3</button>
</div>
}
export default UploadImageToS3WithNativeSdk;

How to send a form input data containing both image and text from React front-end to Express backend using Multer

When I test sending a request containing both image and text grabbbed from user, it comes through to the backend with proper data when I use Postman. Not from React front-end, though. Request does come through but req.body seems to be empty when I console.log it from backend. What am I doing wrong? I am using Multer.
//FRONT-END
import React, { useState } from 'react';
import axios from 'axios';
const ListProperty = (props) => {
const [address, setAddress] = useState('');
const [file, setFile] = useState(null);
const [filename, setFilename] = useState('Choose File');
const handleAddressChange = (evt) => {
setAddress(evt.target.value);
};
const handlePhotoSelect = (evt) => {
setFile(evt.target.files[0]);
setFilename(evt.target.files[0].name);
};
const handleSubmit = async (evt) => {
evt.preventDefault();
const formData = new FormData();
formData.append('address', address);
formData.append('upload', file);
console.log(formData);
try {
axios.post('http://localhost:3000/listproperty', {
headers: { 'Content-Type': 'multipart/form-data' },
body: formData,
});
} catch (err) {
console.log(err);
}
};
return (
<div>
<h2>Property Listing Form</h2>
<span>Provide property address and Photo</span>
<form onSubmit={handleSubmit}>
<input
type="text"
value={address}
onChange={handleAddressChange}
name={address}
placeholder="Enter address"
/>
<br />
<input type="file" onChange={handlePhotoSelect} />
<button>Click to list</button>
</form>
</div>
);
};
export default ListProperty;
//BACK-END
const express = require('express');
const PropertyModel = require('../models/propertyModel');
const router = new express.Router();
const UserModel = require('../models/userModel');
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/images');
},
filename: function (req, file, cb) {
const uniqueName = `${Math.random().toString(32).slice(2)}.jpg`;
req.image = uniqueName;
cb(null, uniqueName);
},
});
const upload = multer({ storage });
router.post(
'/listproperty',
upload.single('upload'),
async (req, res) => {
console.log('hitting Backend router');
const property = new PropertyModel({
...req.body,
owner: req.user._id,
photo: req.image,
});
await UserModel.findByIdAndUpdate(req.user._id, {
$push: { properties: property._id },
});
try {
await property.save();
res.status(200).send(property);
} catch (err) {
console.log(err);
res.status(400).send(err);
}
}
);
module.exports = router;
If you are sending form data in the body you need to use the formidable npm module
you can install it using npm i formidable
then require formidable at top of the file
var formidable = require("formidable");
router.post(
'/listproperty',
upload.single('upload'),
async (req, res) => {
var form = new formidable.IncomingForm();
form.multiples = false;
form.parse(req, async function (err, fields, files) {
/**now here you can get all files in files and fields with fields
in your case you have sent
formData.append('address', address);
formData.append('upload', file);
above two data in form
so you can get your image from files.upload
and address fields.address **/
})
})
In addition, I would suggest you use Axios for api calls
your axios request is not right. axios post request accepts data as a second argument and third argument is for options ( headers etc ),
axios.post('http://localhost:3000/listproperty', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
another thing is your request is not being triggered at all. try setting input type to submit instead of using the button to trigger onSubmit handler of the form.
<form onSubmit={handleSubmit}>
<input
type="text"
value={address}
onChange={handleAddressChange}
name={address}
placeholder="Enter address"
/>
<br />
<input type="file" onChange={handlePhotoSelect} />
<input type="submit" value="Submit" />
</form>

How Can I save a blob file with a form submission using React.js + Django Rest Framework

I am trying to submit a cropped an image generated in a react app using react-image-crop,and save it to a Django Rest Api using Axios.
The app uses React, Redux and Axios on the frontend and Django Rest Framework on the backend.
The form was submitting fine without the file and saving in django without the code for the file added.
Now that the file is added to the form submission the server returns a 400 error.
I suspect that I am not submitting the blob in the correct format to the django server, but I am unsure on how to proceed.
Update: I have used axios below to convert the blob url to a blob and now I am trying to a file that I can submit to a django rest api. The form submits to the django rest API without the file, but when the file is add into the form submission, I receive a 400 error. I have Updated the code to reflect my latest integrations. I have included the code where I set the headers to multipart/form-data. The error seems to be in the file conversion process in the onSubmit() method below.
Here is my relevant code:
Importing the react-image-crop library.
// Cropper
import 'react-image-crop/dist/ReactCrop.css';
import ReactCrop from 'react-image-crop';
Function inside of a react hook:
const AdCreator = ({ addFBFeedAd }) => {
const [title, setTitle] = useState('');
const [headline, setHeadline] = useState('');
const [ad_text, setAdText] = useState('');
const cropper = useRef();
// Cropper
const [upImg, setUpImg] = useState();
const imgRef = useRef(null);
const [crop, setCrop] = useState({ unit: '%', width: 30, aspect: 1.91 / 1 });
const [previewUrl, setPreviewUrl] = useState();
const onSelectFile = e => {
if (e.target.files && e.target.files.length > 0) {
const reader = new FileReader();
reader.addEventListener('load', () => setUpImg(reader.result));
reader.readAsDataURL(e.target.files[0]);
}
};
const onLoad = useCallback(img => {
imgRef.current = img;
}, []);
const makeClientCrop = async crop => {
if (imgRef.current && crop.width && crop.height) {
createCropPreview(imgRef.current, crop, 'newFile.jpeg');
}
};
const makePostCrop = async crop => {
if (imgRef.current && crop.width && crop.height) {
createCropPreview(imgRef.current, crop, 'newFile.jpeg');
}
};
const createCropPreview = async (image, crop, fileName) => {
const canvas = document.createElement('canvas');
const scaleX = image.naturalWidth / image.width;
const scaleY = image.naturalHeight / image.height;
canvas.width = crop.width;
canvas.height = crop.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(
image,
crop.x * scaleX,
crop.y * scaleY,
crop.width * scaleX,
crop.height * scaleY,
0,
0,
crop.width,
crop.height
);
return new Promise((resolve, reject) => {
canvas.toBlob(blob => {
if (!blob) {
reject(new Error('Canvas is empty'));
return;
}
blob.name = fileName;
window.URL.revokeObjectURL(previewUrl);
setPreviewUrl(window.URL.createObjectURL(blob));
}, 'image/jpeg');
});
};
const onSubmit = (e) => {
e.preventDefault();
const config = { responseType: 'blob' };
let file = axios.get(previewUrl, config).then(response => {
new File([response.data], title, {type:"image/jpg", lastModified:new Date()});
});
let formData = new FormData();
formData.append('title', title);
formData.append('headline', headline);
formData.append('ad_text', ad_text);
formData.append('file', file);
addFBFeedAd(formData);
};
return (
The Form portion:
<form method="post" id='uploadForm'>
<div className="input-field">
<label for="id_file">Upload Your Image</label>
<br/>
{/* {{form.file}} */}
</div>
<div>
<div>
<input type="file" accept="image/*" onChange={onSelectFile} />
</div>
<ReactCrop
src={upImg}
onImageLoaded={onLoad}
crop={crop}
onChange={c => setCrop(c)}
onComplete={makeClientCrop}
ref={cropper}
/>
{previewUrl && <img alt="Crop preview" src={previewUrl} />}
</div>
<button className="btn darken-2 white-text btn-large teal btn-extend" id='savePhoto' onClick={onSubmit} value="Save Ad">Save Ad</button>
</form>
Here is the Axios Call:
export const addFBFeedAd = (fbFeedAd) => (dispatch, getState) => {
setLoading();
axios
.post(`http://localhost:8000/api/fb-feed-ads/`, fbFeedAd, tokenMultiPartConfig(getState))
.then((res) => {
dispatch(createMessage({ addFBFeedAd: 'Ad Added' }));
dispatch({
type: SAVE_AD,
payload: res,
});
})
.catch((err) => dispatch(returnErrors(err)));
}
Here Is where I set the headers to multipart form data
export const tokenMultiPartConfig = (getState) => {
// Get token from state
const token = getState().auth.token;
// Headers
const config = {
headers: {
"Content-type": "multipart/form-data",
},
};
// If token, add to headers config
if (token) {
config.headers['Authorization'] = `Token ${token}`;
}
return config;
};
The Model:
class FB_Feed_Ad(models.Model):
title = models.CharField(max_length=100, blank=True)
headline = models.CharField(max_length=25, blank=True)
ad_text = models.CharField(max_length=125, blank=True)
file = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
The crop preview blob:
blob:http://localhost:3000/27bb58e5-4d90-481d-86ab-7baa717cc023
I console.log-ed the Cropped Image after the axios call.
File:
Promise {<pending>}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: undefined
AdCreator.js:169 formData:
FormData {}
__proto__: FormData
As you can see I am trying to submit the blob image file generated by the react-image-cropper, as part of the form data when the form is submitted. I want to save the cropped image to the Django Rest API.
Any suggestions?
you should send it as "Content-Type": "multipart/form-data" to django imageField. So you should convert your blob file appropriately:
let cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL();
let arr = this.cropImg.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let imageCrop = new File([u8arr], 'imagename', { type: mime });
const fd = new FormData();
fd.append("avatar", imageCrop);
// send fd to axios post method.
// You should pass in post request "Content-Type": "multipart/form-data" inside headers.

Resources