Why is my buffer automatically converted to a nested array - node.js

My buffer format is being changed while trying to retrieve it from the MongoDB database, I'm not sure which step of the process is changing it.
I defined the mongoose schema as a buffer:
var mealSchema = mongoose.Schema({
picture: Buffer,
It looks like a what I expected in the database, a normal buffer:
{ "_id" : ObjectId("5691d1f73131c0db2b056447"), "picture" : BinData(0,"/9j/4TcORXhpZgAASUkqAKw2AA....
This is the code I used to send the buffer back to the client:
findAllMeals({})
.then(function(meals) {
res.send(200, meals);
This is how the client is receiving it back :
Object {type: "Buffer", data: Array[86690]}
[0 … 9999]
[10000 … 19999]
[20000 … 29999]
[30000 … 39999]
[40000 … 49999]
[50000 … 59999]
[60000 … 69999]
[70000 … 79999]
[80000 … 86689]
length: 86690
It became an array of arrays, it's being stored as a buffer, and being send back an nested arrays.
I also tried converting it to base64 in angular to see if it would convert it, it didn't.
I changed the storage data type in the schema to string, it didn't change anything, There's isn't much else I can think of changing to troubleshoot.

It's not an array of arrays, it's a Buffer object with a big data array (console.log splits it to ease the logging). One solution would be to exclude the pictures and then have a different route to fetch the picture for a specific meal (because express can deal with buffers automatically):
// first route, exclude pictures
app.get('/meals', function(req, res, next) {
findAllMeals({}, {
picture: 0
})
.then(function(meals) {
res.send(200, meals);
});
});
// second route to fetch the picture of a meal
app.get('/meal_picture/:mealId', function(req, res, next) {
findOneMeal({
_id: req.params.mealId
}).then(function(meal) {
res.status(200).send(meal.picture);
});
});
Then, in your html, you can simply write:
<img src='/meal_picture/5691d1f73131c0db2b056447'></img>
Side note, res.send(200, meals) is deprecated, use res.status(200).send(meals).

so I got it to work by changing the data type in the schema to
picture: {data: Buffer, contentType: String},
then in the client side I converted the base
<img ng-src="data:image/JPEG;base64,{{meal.picture}}"/>
and I managed to keep everything in the same REST request !

Related

Can't figure out how to upload file and json in form data react

I'm building a MERN stack web app and I want the users to be able to upload post with an image and post data.
The data looks like this:
title: 'The war in Ukraine continues',
text: 'Volodymyr Horbenko is the latest official to lose his job after Mr Zelensky said bosses failed to root out pro-Russian elements in the agency',
sources: [
'https://www.bbc.com/news/world-62223264',
'https://www.theguardian.com/world/live/2022/jul/19/russia-ukraine-war-live-news-putin-and-erdogan-to-meet-us-weaponry-stabilising-frontlines-ukraine-military-chief-says',
],
locations: [
{
description: 'boutcha',
type: 'Point',
coordinates: [-122.479887, 38.510312],
},
{
description: 'kyiv',
type: 'Point',
coordinates: [-122.582948, 38.585707],
},
],
};
now I also want to upload a picture this is just a photo that comes from an input.
I know that I should use formData to send the picture and i've tried uploading the data as well like this:
const form = new FormData();
form.append('data', JSON.stringify(data));
form.append('imageCover', fileInputRef.current.files[0]);
axios.post('/api/v1/stories/', form);
but when it comes to the backend I haven't been able to figure out how to retrieve the data into the request body. I've tried using multer app.use(multer.single('data'));(which works fine for the pictures) and app.use(express.json({ type: 'multipart/form-data' }));
but I get Error: Unexpected end of form, and Error:Unexpected token - in JSON at position 0 errors respectively.
also, if there is another way of sending both form data and files i'd like to hear about it, keep in mind that because there are nested objects I can't simply write form.append(key,value) for each pair in the data
app.post("/api/v1/stories", multer().single("imageCover"), function(req, res) {
var file = req.file;
var data = JSON.parse(req.body.data);
});

Can one field have many value?

For example, I have a Scheme like this:
var User_schema = new Schema({
name: string,
image:{
avartar_image: String,
thumbnai_image: String,
story_image: String
}
});
Is it possible to create one field have many value like that in nodejs and mongoose? And one more, how can I store the image into mongodb and can push back to client using mongoose?, I store image on mongodb with Buffer, like this :
var Imgschema = new Schema({
img: Buffer;
});
And if send it to the user when user request a link on server, I do like this:
app.get('/', function (req, res, next) {
Imgschema.findById('5cdnjwjadhad_ad14', function (err, object) {
res.contentType('image/png');
res.send(object.image, {imageToClient: object.image});
});
});
It will send the image to the user with full screen, but I want the image display inside the tag with view engine, something like this:
<img src="{{imageToClient}}">
Any one help please, Thanks.
Of course you can. There are two ways to store a nested object with mongoose. You can use a subdocument, or just nest the object like you did. The difference between two methods is only a middleware and autogenerated _id field. Here is a good answer: Mongoose subdocuments vs nested schema
Storing images as a buffer in a document is not a good idea, but if you want, you can use base64 decoding to show image in browser.
<img src="{{ 'data:image/png;base64,' + imageBuffer.toString('base64') }}" />

Passing Route Parameter to Mongoose Query Express

Basically I'm trying to find a way in which to find a way to plug req.params.name into a find() query.
I have tried:
Trying to pass through my req.params variable in my find() object parameter Card.find({cardName: req.params.name}, callback) and any other possible variance of that.
I've tried a static method for findByName in which I just did Card.findByName(req.params.name, callback);
When I do console.lo(req.params.name) it returns the name of the card; however, when I got to show.ejs I do console.log(cardstatsok.cardName) it comes back undefined.
I've searched here on Stack Overflow, I've checked my Udemy message board, and I've tried finding any tutorial on passing route parameters to a find query, and alas, nothing.
Here's my code:
My schema and model:
var cardSchema = new mongoose.Schema({
cardName: String,
id: Number,
cardSet: String,
image: String,
ability: String
});
var Card = mongoose.model("Card", cardSchema);
My route for my single card page:
app.get("/cards/:name", function(req, res) {
Card.find({"cardName":req.params.name}, function(err, cardInfo){
if(err){
res.redirect("/cards");
console.log(err);
} else {
res.render("show", {cardstatsok: cardInfo});
}
});
});
When I do console.log(cardInfo) it returns many objects since I used "Forest" and the copy of the Magic: The Gathering card Forest has been printed many times. Here's one, though:
{ _id: 5a85bb554c8fff0c04f15b8e,
cardName: 'Forest',
id: 243461,
cardSet: 'Duel Decks: Knights vs. Dragons',
image: 'http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid=243461&type=card',
__v: 0 }
find() returns an array which means cardstatsok is an array.
So console.log(cardstatsok.cardName) won't work. Use console.log(cardstatsok[0].cardName) instead for the first card or console.log(cardstatsok) for everything. If you want to print all the card names you have to loop over the array.
To find only one card you can use findOne() instead.

How can I get the original order of files in the request?

I'm using express and multer. In the following the files are in a random order. The order of the files is important to this particular request. How can I obtain the original order from the request? All I need is an array of original names.
/* POST Put user items. */
router.post('/put', upload.array('useritem'), function (req, res, next) {
// req.files is always in a different order.
});
Node doesn't guarantee the index order of arrays. See this question for more info: Nodejs Order of properties guarantee
As pointed out emphatically, my statement above is incorrect. The correct statement would have been that you should not relay on the order of properties in an object if you have used JSON.stringify() to convert the object to JSON and then sent it as the payload of a request.
Although my original statement is apparently incorrect, it is a problem I've run into myself — where array values show up in the wrong order on my server — and I solved it like illustrated below. Amit's suggestion in the comments above could be the source of the problem. Specifically that it's your client side javascript that's not originally entering the files in the order you'd expect.
Whatever the cause, you'll have to do some work on the client side to add an 'artificial' index to each file and then pass them through as an array of objects.
So instead of this:
var files = ['file1.jpg','file2.jpg','file3.jpg'];
You'll need to pass an object to your backend like so:
var files = [
{ index : 0, file : 'file1.jpg' },
{ index : 1, file : 'file2.jpg' },
{ index : 2, file : 'file3.jpg' },
];
And then you'll need to sort the array of objects once it's received by your backend instance, like so:
files.sort(function(a, b) {
return a.index - b.index;
});
And then you can loop through the files array and do what you will:
var justFiles = [];
async.each(files, function(file, iterate){
justFiles.push(file.file);
return iterate();
}, function(result){
console.log(justFiles);
});
And the output would be:
'file1.jpg','file2.jpg','file3.jpg'

Mongoose - Go to next element

I am trying to iterate over different IDs over a collection in nodejs. Something that would work like the following code:
//Callbacks removed for readability
var thisPost = mongoose.model('Post').findOne({tags: 'Adventure'});
console.log(thisPost.title); // 'Post #1 - Adventure Part 1'
var nextPost = thisPost.next({tags: 'Adventure');
console.log(nextPost.title); // 'Post 354 - Adventure Part 2'
Best idea so far would be to add a linkedlist to my schema so I could call find() over my next reference to a specific ID but I was hoping for something less 'tricky' that would permit me to use this Mongoose reference (thisPost) as a cursor where my find() could start from.
Thanks
EDIT: The iteration is meant to work over multiple page queries. Better example:
//Callbacks removed for readability
//User 'JohnDoe' visits the website for the first time
var thisQuote = mongoose.model('Quote').findOne().skip(Math.rand());
res.send(thisQuote); // On page output, JohnDoe will see the quote 42
//Saving the current quote cursor to user's metadatas
mongoose.model('User').update({user: 'JohnDoe'}, {$set: {lastQuote: thisQuote }});
//User 'JohnDoe' comes back to the website
var user = mongoose.model('User').findOne({user: 'JohnDoe});
var thisQuote = user.lastQuote.next();
res.send(thisQuote); // On page output, JohnDoe will see the quote 43
//Saving the current quote cursor to user's metadatas
mongoose.model('User').update({user: 'JohnDoe'}, {$set: {lastQuote: thisQuote }});
//And so on...
You might look into Mongoose's streaming capabilities:
var stream = mongoose.model('Post').find({tags: 'Adventure'}).stream();
// Each `data` event has a Post document attached
stream.on('data', function (post) {
console.log(post.title);
});
QueryStream, which is what stream() returns, inherits from Node.js' Stream, so you can do some interesting things using pause and resume if you need to.
[Edit]
Now that I understand your question a bit more, I would say that a QueryStream is probably not what you want to use. I worked on this a bit today and got a working solution at https://gist.github.com/3453567; just clone the Gist (git://gist.github.com/3453567.git), run npm install and then node index.js and you should be able to visit the site at http://localhost:3000. Refreshing the page should give you the "next" quote, and when you reach the end it should wrap around.
This works because of a couple things:
First, we save a reference to a user's "last viewed" quote in their data:
var UserSchema = new mongoose.Schema({
user: String,
lastQuote: { type: mongoose.Schema.Types.ObjectId, ref: 'Quote' }
});
Now when we do User.findOne().populate('lastQuote'), the lastQuote attribute on the User that gets returned will be an actual Quote object referred to by the value of the field stored in MongoDB (which is an ObjectId).
We can call next() on this Quote object because of the following code:
QuoteSchema.methods.next = function(cb) {
var model = this.model("Quote");
model.findOne().where('_id').gt(this._id).exec(function(err, quote) {
if (err) throw err;
if (quote) {
cb(null, quote);
} else {
// If quote is null, we've wrapped around.
model.findOne(cb);
}
});
};
This is the part that finds the next quote or else wraps around to the first quote.
Take a look at the code and let me know if you have any questions.

Resources