Progress bar upload with angular and express - node.js

I'm trying to build a file uploader with a progress bar. I found angular's way of listening to progress events (reportProgress) as simple and useful when it comes to the documentation. But I had trouble implementing it in my project. For some reason when I upload the image the progress shows 100% as soon as the request is sent and doesn't show real progress.
My service.ts
public upload(file) {
const formData = new FormData();
formData.append('file', file);
return this.http.post<any>(`${config.apiUrl}/articles/image-upload`, formData, {
reportProgress: true,
observe: 'events'
}).pipe(map((event) => {
switch (event.type) {
case HttpEventType.UploadProgress:
const progress = Math.round(100 * event.loaded / event.total);
return { status: 'progress', message: progress };
case HttpEventType.Response:
return event.body;
default:
return `Unhandled event: ${event.type}`;
}
})
);
}
And in my component
addImg() {
this.articlesService.upload(this.file).subscribe(
(res) => console.log(res),
(err) => console.log(err)
);
}
When it comes to the backend I'm using multer-s3 to upload files to aws clouds with express server.
const upload = multer({
storage: multerS3({
s3,
contentLength: 500000000,
bucket: 'bucketss',
acl: 'public-read',
metadata: function (req, file, cb) {
cb(null, {fieldName: 'TESTING_META_DATA!'});
},
key: function (req, file, cb) {
cb(null, Date.now().toString() + randtoken.uid(16))
}
})
})
const singleUpload = upload.single('file');
router.post('/image-upload', async (req, res) =>{
await singleUpload(req, res, function(err) {
if (err) {
console.log(err)
return res.status(422).send({errors: [{title: 'File Upload Error', detail: err.message}] });
}
return res.json({'imageUrl': req.file.location});
});
});

Related

Saving Images React To Nodejs

I am trying to upload an image from my front end to the backend but it it doesn't send the image in the request
It says that the formdata is empty and it says that there's no image found, where is the problem and how can I fix this error?
Here is the code from the Frontend made in react:
const [userInfo, setuserInfo] = useState({
file:[],
filepreview:null,
});
const handleInputChange = (event) => {
setuserInfo({
...userInfo,
file:event.target.files[0],
filepreview:URL.createObjectURL(event.target.files[0]),
});
}
const [isSucces, setSuccess] = useState(null);
const submit = async () =>{
const formdata = new FormData();
formdata.append('avatar', userInfo.file);
console.log(formdata)
Axios.post("http://localhost:4000/imageupload", formdata,{
headers: { "Content-Type": "multipart/form-data" }
})
.then(res => { // then print response status
console.warn(res);
if(res.data.success === 1){
setSuccess("Image upload successfully");
}
})
}
The code of the Backend made in NodeJS:
const storage = multer.diskStorage({
destination: path.join(__dirname, './temp', 'uploads'),
filename: function (req, file, cb) {
// null as first argument means no error
cb(null, Date.now() + '-' + file.originalname )
}
})
app.post('/imageupload', async (req, res) => {
try {
// 'avatar' is the name of our file input field in the HTML form
let upload = multer({ storage: storage}).single('avatar');
upload(req, res, function(err) {
// req.file contains information of uploaded file
// req.body contains information of text fields
if (!req.file) {
return res.send('Please select an image to upload');
}
else if (err instanceof multer.MulterError) {
return res.send(err);
}
else if (err) {
return res.send(err);
}
const classifiedsadd = {
image: req.file.filename
};
res.send("ok")
});
}catch (err) {console.log(err)}
})
Edit:
Multer is essentially a nodejs router,i.e. a function that can be pipelined between your HTTP request and HTTP response.
I think that you should first make multer analyze your HTTP content and to actually populate the req.file before actually evaluate express parsers do their job.
const storage = multer.diskStorage({
destination: path.join(__dirname, './temp', 'uploads'),
filename: function (req, file, cb) {
// null as first argument means no error
cb(null, Date.now() + '-' + file.originalname )
}
})
let upload = multer({ storage: storage});
app.post('/imageupload', upload.single('avatar'), async (req, res) => {
try {
// 'avatar' is the name of our file input field in the HTML form
// req.file contains information of uploaded file
// req.body contains information of text fields
if (!req.file) {
return res.send('Please select an image to upload');
}
else if (err instanceof multer.MulterError) {
return res.send(err);
}
else if (err) {
return res.send(err);
}
const classifiedsadd = {
image: req.file.filename
};
res.send("ok")
}catch (err) {console.log(err)}
})
I am assuming that your upload code is working. Have you tried to read the HTTP request from your browser to see that the image has been correctly attached to the request?
Because probably the issue lies in the fact that you are not actually parsing the image.
const file = new File(userInfo.file, "avatar.png", {
type: 'image/png' // choose the appropriate
});
const formdata = new FormData();
formdata.append('avatar', file);
console.log(formdata)

uploading files from react to node js with multer

I want to upload files from the form data in react which is being posted by axios like this.
const addNewProduct = () => {
const newProduct = {
name: name,
cost: cost,
size: size,
color: color,
material: material,
discount: discount,
description: description,
category: category
};
const nulls = Object.values(newProduct).filter(p => p === null);
if(nulls.length === 0 && images.imageFiles) {
let productFormData = new FormData();
productFormData.append('productInfo', JSON.stringify(newProduct));
productFormData.append('productImages', images.imageFiles);
const addUrl = "http://localhost:8080/cpnl/addproduct";
axios({
method: "POST",
url: addUrl,
data: productFormData,
headers: { "Content-Type": "multipart/form-data" }
})
.then((response) => {
console.log(response.data.msg);
})
.catch((response) => {
console.error(response);
});
}else {
Notiflix.Notify.Warning("Check your inputs!");
console.log(nulls);
console.log("product: \n" + JSON.stringify(newProduct));
}
};
then I want to upload images with multer to images folder. this is my code:
const storage = multer.diskStorage({
destination: "./public/images",
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).array("productImages", 5);
function checkFileType(file, cb) {
// Allowed ext
const filetypes = /jpeg|jpg|png/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: Images Only!');
}
}
//receive form data from front-end and add new product to database
router.post('/addproduct', async (req, res) => {
upload(req, res, (err) => {
if(err) {
res.status(400).json({
msg: err
});
} else {
if(req.files == undefined) {
res.status(400).json({
msg: "Error: No file selected! please contact the developer."
});
} else {
data = req.body.productInfo;
res.status(200).json({
msg: "Files uploaded!"
});
console.log( "images: " + req.files);
console.log("data" + data);
}
}
});
});
first problem: I'm getting image files inside req.body.productImages and not inside req.files
second problem: when I send the request node js throws me this error:
TypeError: upload is not a function
why everything is messed up!?
Edit: I restarted the server and now I'm getting data but the files are not being uploaded. no error is shown.
UPDATE: second problem fixed
First Problem : You have used .array("productImages", 5); in your upload function. use .array("files", 5); to get the file in req.files.
Second Problem : I guess there is some typo error in your code upload(req, res, (err)... there is one extra bracket it should be only upload(req,res,err )...

NodeJs >Express > AWS > S3 > getting emplty res.send() intested of object probably a Promise delay

I am trying to work with, I am getting the object in console.log(data,"MongoData--->") but getting the empty object as a response in clint side
Promise {
{ _id: 5f44ed71ae6ebea2d14a9808,
name: 'https://rsboost.s3.amazonaws.com/sn3-1598352752273.png' } } 'MongoData--->'
RouteFile
router.post("/",async function(req, res, next){
ImgUpload(req, res,(error) => {
if (error) {
console.log("errors", error);
res.json({ error: error });
} else {
// If File not found
if (req.file === undefined) {
console.log("Error: No File Selected!");
res.json("Error: No File Selected");
} else {
// If Success
const imageLocation = req.file.location; // Save the file name into database into File model
const data = await CreateFile({name:imageLocation})
console.log(data,"MongoData--->")
res.json(data)
}
}
})
});
Aws Function returning correct data
const ImgUpload = multer({
storage: multerS3({
s3: s3,
bucket: "*****",
acl: "public-read",
key: function (req, file, cb) {
cb(
null,
path.basename(file.originalname, path.extname(file.originalname)) +
"-" +
Date.now() +
path.extname(file.originalname)
);
},
}),
limits: { fileSize: 20000000 }, // In bytes: 2000000 bytes = 20 MB
fileFilter: function (req, file, cb) {
checkFileType(file, cb);
},
}).single("fileImage");
Problem: getting the desired response with data but under-promise, Need help to write promise as I tried async-await but it's not working
Thanks in advance
solved it by myself > in route file > .then()=> is the solution for waiting for the promise, little thing I missed
const data = CreateFile({ name: imageLocation }).then((data) => {
res.send(data);
});

How to upload a file to the server (Node.js)

Don't upload photos to the server, how to solve this problem?
on the page index.ejs a photo gallery should be generated from the added entries. The entry contains a photo. The entry is added, but the photo doesn't load.
project (GitHub)
app/routes.js:
var upload = multer({
storage: storage,
limits: {fileSize: 7},
fileFilter: function (req, file, cd) {
checkFileType(file, cd);
}
}).single('filePhoto');
function checkFiletType(file, cd) {
const fileTypes = /jpeg|jpg/;
const extname = fileTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = fileTypes.test(file.mimetype);
if (extname && mimetype) {
return cd(null, true);
} else {
cd('Error: only JPEG or JPG!')
}
var Photo = require('../app/models/photo');
module.exports = function (app, passport) {
app.get('/', function (req, res,next) {
Photo.find({}, function (error, photos) {
var photoList = '';
res.render('index.ejs', {photoList: photos});
});
});
}
app.post('/addPhoto', function (req, res, next) {
next();
}, function (req, res) {
var newPhoto = new Photo(req.body);
newPhoto.save().then(function (response) {
console.log('here', response);
res.status(200).json({code: 200, message: 'OK'});
}).catch(function (error) {
console.error('new photo error', error);
});
},function (req, res) {
Photo.find({}, function (error, photos) {
res.send('index.ejs', {
photoList: photos
});
});
});
};
You need to pass your upload var as middleware to your upload route.
Here is a snippet from how I have done it previously:
// Route:
const storage = multer.memoryStorage()
const upload = multer({ storage: storage })
router.post('/upload', upload.single('photo'), ImageController.upload);
// Image Controller:
upload(req, res){
console.log("file", req.file)
}
When I post my image, I make sure I call it photo to match the key word I used in my multer middleware:
So I create my form data like so:
const formData = new FormData()
formData.append('photo', {
uri: data.uri,
type: 'image/jpeg',
});
axios.post(`${SERVER}/images/upload`,
formData: formData,
{ headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => console.log("response", response))
.catch(err => console.log('err', err))

Amazon S3 bucket file upload response is not available

I'm using multer ,aws-sdk and multer-s3 packages along with express.
when users edit profile user may change profile picture /avatar or not.
I've passed multer object
multer({storage:
multer.memoryStorage()}).single('profileHeroImageEdit')
if a file with current request then I will upload the file to s3 bucket but I am not getting any response from upload_replace where req.file.location will provide the url of S3 bucket (file's location).
And Inside upload_replace I can get the file I am trying to upload(req.file) but I want the location of uploaded file to S3 bucket .
What I'm missing ? Help will be appreciated
router.put("/:id",multer({ storage:
multer.memoryStorage()}).single('profileHeroImageEdit'),
middleware.checkProfileOwnership,function(req, res){
if(isFileExists(req)==false){
delete req.body.profileHeroImage
}
else{
console.log('file has')
var upload_replace = multer({
limits:{
fileSize:MAX_FILE_SIZE,
files:1
},
storage: multerS3({
s3: photoBucket,
bucket: BucketName,
acl: 'public-read',
metadata: function (req, file, cb) {
cb(null, { fieldName: file.fieldname });
},
key: function (req, file, cb) {
cb(null,Date.now().toString())
}
})
}).single('profileHeroImageEdit')
upload_replace(req, res, function (err,log) {
console.log('request log')
console.log(req.file.location)
console.log()
});
}
Profile.findByIdAndUpdate(req.params.id, req.body.profile, function(err, updatedProfile){
if (err){
res.redirect("/profiles");
} else {
res.redirect("/profiles/" + req.params.id);
}
});
});
function isFileExists(request){
if(request.file)
{
return true
}
else{
return false
}
}
I have whole code using multer and aws-sdk
include this files and npm install all
//aws s3 packages
const aws = require("aws-sdk");
const multerS3 = require("multer-s3");
const multer = require("multer");
const path = require("path");
then
//profile image upload start
const s3 = new aws.S3({
accessKeyId: "***",
secretAccessKey: "***",
Bucket: "***"
});
//Singe profile image upload
const profileImgUpload = multer({
storage: multerS3({
s3: s3,
bucket: "***",
acl: "public-read",
key: function(req, file, cb) {
cb(
null,
path.basename(file.originalname, path.extname(file.originalname)) +
"-" +
Date.now() +
path.extname(file.originalname)
);
}
}),
limits: { fileSize: 2000000 }, // In bytes: 2000000 bytes = 2 MB
fileFilter: function(req, file, cb) {
checkFileType(file, cb);
}
}).single("profileImage");
// getExtension of file by this function
function getExtension(filename) {
var parts = filename.split(".");
return parts[parts.length - 1];
}
//checkfile type of input function
function checkFileType(file, cb) {
const ext = getExtension(file.originalname);
switch (ext.toLowerCase()) {
case "jpeg":
case "jpg":
case "png":
case "gif":
return cb(null, true);
}
cb("Error: Images Only!");
}
router.post(
"/image",
passport.authenticate("jwt", { session: false }),
(req, res) => {
profileImgUpload(req, res, error => {
if (error) {
res.json({ error: error });
} else {
//here we can get req.body
const userDp = {};
//end of getting values
// If File not found then dont store anything
if (req.file !== undefined) userDp.dpUrl = req.file.location;
// Save the file name into database into profile model
User.findOne({ email: req.user.email }).then(user => {
if (user) {
// Update
User.findOneAndUpdate(
{ email: req.user.email },
{ $set: userDp },
{ new: true }
).then(user => res.json(user));
} else {
res.json({ msg: "not able not update data" });
}
});
}
});
}
);
3.need to send data from react frontend by using
const data = new Formdata();
data.append()
and by including headers also
Add a listener for when the OutgoingMessage to upload the photo to S3 is completed. The on-finished library is handy for this.
const onFinished = require('on-finished');
const print = process._rawDebug;
uploadReplace(req, null, function (err) {
onFinished(req, function () {
if (err) print(err);
print(req.file.location);
// Other things can be done here
})
});

Resources