I'm really a newbie with Angular, MongoDB, so I'm asking for your help !
I'm trying to display images on an Angular App
Images are saved using Gridfs and sent to the app with fs.createReadStream
//The model
const ProfilePic = mongoose.Schema(
{
_id : mongoose.Schema.Types.ObjectId,
lenght: Number,
chunksize: Number,
uploadDate: Date,
filename: String,
md5: String,
contentType: String
}
);
// Create storage engine
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
const filename = file.originalname;
const fileInfo = {
filename: filename,
bucketName: "ProfilePic"
};
resolve(fileInfo);
});
}
});
const upload = multer({ storage });
router.post("/setProfilePic", upload.single("picture"), (req, res) => {
console.log("image received");
return res.status(200).json({err: "no"})
});
router.get('/getProfilePic/:filename', (req, res) => {
gfs.collection('ProfilePic'); //set collection name to lookup into
/** First check if file exists */
gfs.files.find({filename: req.params.filename}).toArray(function(err, files){
if(!files || files.length === 0){
return res.status(404).json({
responseCode: 1,
responseMessage: "error"
});
}
// create read stream
var readstream = gfs.createReadStream({
filename: files[0].filename,
root: "ProfilePic"
});
// set the proper content type
res.set('Content-Type', files[0].contentType)
// Return response
return readstream.pipe(res);
});
});
// method used to fetch the image
async getProfilePic(name) {
return this.http.get(this.url + "user/getProfilePic/" + name);
}
Here's the error I get on the application :
Unexpected token � in JSON at position 0
What is wrong with my code ?
The client/browser is trying to parse the response as json since that's the default. Just pass an object with a responseType attribute set to 'blob' and it should work.
// method used to fetch the image
async getProfilePic(name) {
return this.http.get(this.url + "user/getProfilePic/" + name, {responseType: 'blob'});
}
https://angular.io/api/common/http/HttpRequest#responseType
Related
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)
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 )...
I'm trying to do the following in Node.js using express router, Multer-S3, Multer, AWS and Mongodb.
I want to:
1: Check if filetype is image, price is number etc (some kind of quality check)
2: If above true, upload image to S3 to get Image url
3: If Image Url was generated, upload to Mongodb, including the generated image url..
Trying with below code but can only get one of these to work at same time..
const express = require("express");
const router = express.Router();
const shopController = require("../controllers/shop");
router.post(
"/shop/create/:shopId",
shopController.creatingShop,
shopController.createShopItem
);
const ShopItem = require("../models/shopitem"); //Mongoose Schema
const multer = require("multer");
const fileview = multer().single("file1"); //Trying to use this to view file before uploading to S3
const uploader = require("../services/file-upload");
const singleUpload = uploader.single("file1"); //Using this to upload to S3
exports.createShopItem = (req, res, next) => {
fileview(req, res, function (err) {
const file = req.file;
const title = req.body.title;
const price = req.body.price;
const description = req.body.description;
const location = req.body.location;
const user = "OrreSnorre";
if (
file.mimetype != "image/jpeg" &&
file.mimetype != "image/jpg" &&
file.mimetype != "image/png"
) {
return next(new Error("invalid file type"));
}
if (file.size > 2500000) {
return next(new Error("Your image is to big. Maximum 2.5mb"));
}
next();
console.log(
"Here I want to add upload text to mongoDb... including URL from S3 after it is generated"
);
});
exports.creatingShop = (req, res, next) => {
singleUpload(req, res, function (err) {
console.log(req.file);
// res.json({ "image-url": req.file.location });
});
next();
};
Anyone got ideas? Or examples that work?
Best regards,
Oscar
There are 2 ways to do this, either you can use only multer or multer-s3.
For simplicity, I will show you the way using only multer.
Flow of processing as follow:
Multer process and save to local
You read from local, and upload to s3 using s3 SDK (You should explore how to remove the file after upload as well, but I wont clutter you with this logic here)
If upload is successful, you retrieve the URL and pass it to your MongoDB.
// Make "temp" directory as multer.diskStorage wont create folder
fs.mkdir('./temp', { recursive: true }, (err) => {
if (err) throw err;
});
const PORT = parseInt(process.argv[2]) || parseInt(process.env.PORT) || 3000;
// Multer
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './temp');
},
filename: function (req, file, cb) {
let extArray = file.mimetype.split('/');
let extension = extArray[extArray.length - 1];
cb(null, new Date().getTime() + '.' + extension);
},
});
const upload = multer({ storage: storage });
const endpoint = new AWS.Endpoint(AWS_S3_HOSTNAME);
const s3 = new AWS.S3({
endpoint,
accessKeyId: AWS_S3_ACCESSKEY_ID,
secretAccessKey: AWS_S3_SECRET_ACCESSKEY,
});
// Get the uploaded file in local here
const readFile = (path) =>
new Promise((resolve, reject) =>
fs.readFile(path, (err, buff) => {
if (null != err) reject(err);
else resolve(buff);
})
// Upload to AWS S3 here
const putObject = (file, buff, s3) =>
new Promise((resolve, reject) => {
const params = {
Bucket: AWS_S3_BUCKET_NAME,
Key: file.filename,
Body: buff,
ACL: 'public-read',
ContentType: file.mimetype,
ContentLength: file.size,
};
s3.putObject(params, (err, result) => {
if (null != err) reject(err);
else resolve(file.filename);
});
});
);
const mongoClient = new MongoClient(MONGO_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
app.post('/api/post', upload.single('imageFile'), async (req, res) => {
readFile(req.file.path)
.then((buff) =>
// Insert Image to S3 upon succesful read
putObject(req.file, buff, s3)
)
.then((results) => {
// build url of the resource upon successful insertion
const resourceURL = `https://${AWS_S3_BUCKET_NAME}.${AWS_S3_HOSTNAME}/${results}`;
const doc = {
comments,
title,
ts: new Date(),
image: resourceURL, // Your URL reference to image here
};
// Insert to your mongoDB
mongoClient
.db(MONGO_DB)
.collection(MONGO_COLLECTION)
.insertOne(doc)
.then((results) => {
// delete the temp file when no error from MONGO & AWS S3
fs.unlink(req.file.path, () => {});
// return the inserted object
res.status(200).json(results.ops[0]);
})
.catch((error) => {
console.error('Mongo insert error: ', error);
res.status(500);
res.json({ error });
});
})
.catch((error) => {
console.error('insert error: ', error);
res.status(500);
res.json({ error });
});
}
I'm trying to upload an array of photos to a server but the req.files array always shows up empty when it gets there.
req.body displays the array as expected.
The images are added through a Dropzone component. (I've tried switching this for a standard input but they both seem to pass files the same way)
<Dropzone
onDrop={onDrop}
onSubmit={uploadPhotos}
maxFiles={20}
inputContent="Drop 20 Images"
inputWithFilesContent={files => `${20 - files.length} more`}
/>
The images are applied to FormData with the name image files are appended before being sent via an Axios POST request with multipart/form-data headers set.
export const uploadPhotos = (files) => {
const formData = new FormData();
for (let i = 0; i < files.length; i += 1) {
formData.append("image[]", files[i]);
}
const config = {
headers: {
'Content-Type': `multipart/form-data`
}
}
return async (dispatch, getState) => {
try {
const response = await axios.post('/api/kite/upload',
formData, config)
.then(function(response) {
console.log(response.data);
dispatch({
type: ORDER_CHANGE,
payload: response.data
});
});
} catch (err) {
console.log(err);
} finally {
console.log('done');
}
}
}
once passed to the server only req.body seems to contain any data and req.files is empty despite using Multer middleware as the second parameter. Once passed to files.map() items are undefined undefined, presumably because req.files is an empty array.
var multer = require('multer');
var AWS = require('aws-sdk');
AWS.config.setPromisesDependency(bluebird);
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'upload')
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now())
}
});
const upload = multer({
storage: storage
}).array('image');
router.post('/upload', upload, function (req, res) {
const file = req.files;
let s3bucket = new AWS.S3({
accessKeyId: IAM_USER_KEY,
secretAccessKey: IAM_USER_SECRET,
Bucket: 'BUCKETNAME'
});
s3bucket.createBucket(function () {
let Bucket_Path = 'https://console.aws.amazon.com/s3/buckets/BUCKETNAME?region=eu-west-1';
var ResponseData = [];
file.map((item) => {
// item.x are all undefined
var fileStream = fs.createReadStream(filePath);
var params = {
Bucket: Bucket_Path,
Key: item.originalname,
Body: item.buffer,
ACL: 'public-read'
};
s3bucket.upload(params, function (err, data) {
if (err) {
res.json({ "error": true, "Message": err});
} else{
ResponseData.push(data);
if(ResponseData.length == file.length){
res.json({ "error": false, "Message": "File Uploaded SuceesFully", Data: ResponseData});
}
}
});
});
});
});
My end goal is to pass the images to an Amazon S3 bucket. I don't think it impacts this since there is no data to send but I've included it incase it has somehow affecting this.
I've been through lots of other similar Stack Overflow questions and medium post and the main three resolutions to this issue seem to be included in the flow above.
Append file name to items of FormData array
Set POST request headers
Include Multer middleware in express parameter
Can anyone help me figure out why req.files is an empty array?
It might be that Dropzone isn't processing the files. Try adding this to the uploadPhotos function:
const acceptedFiles = myDropzone.getAcceptedFiles() // "myDropzone" is just the Dropzone instance
for (let i = 0; i < acceptedFiles.length; i++) {
myDropzone.processFile(acceptedFiles[i])
}
I have setup multer as middleware on a route. When that route is hit, multer doesn't download the file. This is what console.log reports:
{ firstname: 'foo',
lastname: 'foo',
username: 'foo10',
password: 'test1234',
file: '/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAAB.....
}
I have the following setup for multer:
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, '../images/profile');
},
filename: function(req, file, cb) {
cb(null, req.body.username + '.jpeg');
}
});
var upload = multer({storage: storage});
router.post('/auth/signup', upload.single('file'), function(req,res) {
console.log(req.body);
});
So the issue is that instead of saving the file as a file it simply treats it as another key-value pair in the form.
To clarify further, i obtain this image from cordova API:
http://ionicframework.com/docs/v2/native/camera/
import { Camera } from 'ionic-native';
Camera.getPicture(options).then((imageData) => {
// imageData is either a base64 encoded string or a file URI
// If it's base64:
let base64Image = 'data:image/jpeg;base64,' + imageData;
let formData: FormData = new FormData(),
xhr: XMLHttpRequest = new XMLHttpRequest();
formData.append("file", base64Image);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
observer.next(JSON.parse(xhr.response));
observer.complete();
} else {
observer.error(xhr.response);
}
}
};
xhr.open('POST', 'http://localhost:8080/auth/signup', true);
xhr.send(formData);
}, (err) => {
// Handle error
});
The file data is being uploaded as a Base64 string, which suggests that it's not being uploaded as a proper File form data field. That's why multer treats it as a regular field, and doesn't attempt to save it to a file.
In this answer, some client side code is presented to handle data-URI strings (like base64Image) to the right format to upload to the server.