associating a GridFS-Stream file to a Schema with Mongoose - javascript

I am writing an API for my application, using Mongoose, Express, and GridFS-Stream. I have a Schema for the articles the user will create:
var articleSchema = mongoose.Schema({
title:String,
author:String,
type: String,
images: {type: Schema.Types.ObjectId, ref: "fs.files"},
datePublished: { type: Date, default: Date.now },
content: String
})
var Article = mongoose.model("article", articleSchema, "articles");
and my grid-fs set up for when a user uploads an image:
api.post('/file', fileUpload.single("image"), function(req, res) {
var path = req.file.path;
var gridWriteStream = gfs.createWriteStream(path)
.on('close',function(){
//remove file on close of mongo connection
setTimeout(function(){
fs.unlink(req.file.path);
},1000);
})
var readStream = fs.createReadStream(path)
.on('end',function(){
res.status(200).json({"id":readStream.id});
console.log(readStream);
})
.on('error',function(){
res.status(500).send("Something went wrong. :(");
})
.pipe(gridWriteStream)
});
Right now it's set up to when the user chooses an image, it automatically uploads it via gridfs-stream, puts it in a temp folder, then deletes it when it is uploaded to the mongo server, and in the console returns what the ObjectId is. Well thats all find and dandy, but we need to associate this ID with the articleSchema, so when we call that article in the app, it will display the associated image.
on our creation/update of an article when the user hits submit:
createArticle(event) {
event.preventDefault();
var article = {
type: this.refs.type.getValue(),
author: this.refs.author.getValue(),
title: this.refs.title.getValue(),
content: this.refs.pm.getContent('html')
};
var image = {
images: this.refs.imageUpload.state.imageString
};
var id = {_id: this.refs.id.getValue()};
var payload = _.merge(id, article, image);
var newPayload = _.merge(article, image)
if(this.props.params.id){
superagent.put("http://"+this.context.config.API_SERVER+"/api/v1.0/article/").send(payload).end((err, res) => {
err ? console.log(err) : console.log(res);
});
} else {
superagent.post("http://"+this.context.config.API_SERVER+"/api/v1.0/article").send(newPayload).end((err, res) => {
err ? console.log(err) : console.log(res);
this.replaceState(this.getInitialState())
this.refs.articleForm.reset();
});
}
},
So what I need it to do, is call the ID, of the image I just uploaded to the images section of my schema when the user hits submit on the creation of an article. I've tried doing a readstream on submit, but again, the problem is I can't get the ID, or the filename, to be able to associate it.
They are getting stored in the mongo database, it creates fs.files and fs.chunks, but for the life of me I can't figure out how to get that data and attach it to a schema, or just even get the data out, without knowing the ObjectId.
So how do I call out the objectid from fs.files or fs.chunks to attach it to the schema? and in the schema how do I reference the fs.files or chunks? so it knows what the objectid is associated with?
I can provide anymore data, if what I have is to vague, I have a nasty habit of doing that. sorry.

So I ended up solving my problem, might not be the best solution, but it works until I can get a better solution.
in the API changed
res.status(200).json({"id":readStream.id});
to
res.status(200).send(readStream.id);
in my component, I then set the state to the response.body, which will set the state of the id of the image uploaded. So in the main view, i reference the image uploading component, and set the image state of my view to the id state of my component, and viola, I now have the id in my database, associated with the newly created article.
the problem i then ran into was, it didn't know what to reference. so I attached the API URL to the id, and it acts like it is referencing a URL img, and renders the image correctly.
Again, this may not be the best way to go about this, in fact, I am pretty sure it isn't, but It is whats working for now until I can either reference the database correctly, or create a new component that just stores all the images on server and reference them that way, much like wordpress.

Related

Node js multer send image without update

friends, I am developing a project with node js, I have a form in my hand, in this form I send pictures and texts to the database (MongoDB), there are no problems with them, including uploading images and updating and sending images.
But the user may want to update and send only the articles, and in this case, he may not want to update the picture. I think about this, I want to send the data to the database without updating the picture, I actually did this, but this time I cannot update the picture while it is in the update process, how can I solve this problem? can you help?
Thank you from now.
The code block I did the update process is as follows
const medicine = await Medicine.findOne({ _id: req.params.id });
medicine.name = req.body.name;
medicine.price = req.body.price;
medicine.medicineType = req.body.medicineType;
medicine.description = req.body.description;
if (req.body.image) {
medicine.image = '/uploads/' + req.file.filename;
} else if (!req.body.image) {
}else{
}
console.log(req.body);
medicine.save();

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client. Attempting to write Mongo ObjectID to cookies

I will preface this by saying I understand it is not best practice to put a userID in cookies, but this is an extremely simple application that is just supposed to display to the same user the number of times they have visited the site, so I'm just rolling with this implementation to keep things simple.
I'm using express, node, and mongoDB to handle this. What I'm trying to do is create and save a new instance of my User class, and then in the promise chain (where the Mongo ObjectID is returned), I'd like to write that ID to cookies so I can keep track of how many times they have come to the site.
It seems like I am receiving the Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers because I am trying to set cookies in the then statement which is completed after the headers are already sent off. I don't really understand this though, because I don't call setHeaders anywhere else.I have thought about creating a randomly generated number within the server, but that seems clunky as I can't guarantee it won't generate an existing ID. Does anyone know how I can get around this issue, or am I taking the complete wrong approach here?
User schema:
var UserSchema = new Schema({
timesSeen: { type: Number, default: 0 },
});
The problem code
router.get("/", function (req, res, next) {
// Read cookies
let cookies = parseCookies(req.headers.cookie);
// If userID cookies is not set, this is a new user (or one visiting from another browser)
if (cookies.userID === undefined) {
// Create a new instance of a user and save to mongoDB
var newUser = new User({});
newUser
.save()
.then((newUser) => {
console.log("newUser");
// This is the line that is giving me problems.
res.setHeader("Set-Cookie", [
`userID=${newUser._id}`,
"timesSeen=0",
]);
})
.catch((err) => {
if (err) {
console.log(err);
return;
}
});
} else {
let timesSeen = Number(cookies.timesSeen) + 1;
// let timesSeen = parseInt(cookies.timesSeen)++;
// console.log("times", timesSeen);
res.setHeader("Set-Cookie", [`timesSeen=${timesSeen}`]);
res.render("index", {
title: "Express",
});
}
});

Mongoose how to update array from child to parent

I have following schema for Audio.
const AudioSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
uploaderId: {
type: String,
required: true
}
});
Instead of referencing the User, I just store the User's _id as uploaderId.
In my User schema I also have audioFiles: [Audio] array for all audio files that user has uploaded.
const UserSchema = new mongoose.Schema({
...,
audioFiles: [Audio]
});
When I try to update my AudioSchema from my REST Api, I can change all the properties and that works, but after saving this Audio model those changes doesn't affect the User model.
Now I created a new branch and try to change uploaderId to UserSchema. But I wonder is there a solution for this without referencing the UserSchema
I managed to do this with help of MongooseArray.prototype.pull method.
Steps for solving this problem is really easy.
First I get the User that is associated with AudioModel.uploaderId, then I used the user.audioFiles.pull() method. Correct code is below.
let user = await UserService.getUser(userId);
await user.audioFiles.pull({
_id: audioId //audioId is the id which i'm trying to remove from array
});
await user.save();
I also added try-catch block to handle errors.
Anyone having this kind of issue can use the link below to get more information about MongooseArray.prototype.pull method.
Also you can check the other answers in this post.

Meteor: How to store and retrive file in a mongodb collection?

I try to save the content of uploaded file in a collection:
export const Files = new Mongo.Collection('files');
Meteor.methods({
'saveFileToDB': function (buffer) {
Files.insert({ data:buffer,
createdAt: new Date(),
owner: this.userId,
username: Meteor.users.findOne(this.userId).username
});
},
});
In another file I want to retrieve the saved file. First, I don't know what id I should to pass it. Suppose that there is one file in the collection or I want the first one, or the ones owned by the current user. I tried, I passed fileId as 1 but it didn't work. I don't know actually the question marks below:
import {Files} from "./files";
Meteor.methods({
'slides.readFileFromDB'(fileId) {
if (Files.???) { //contain any data
const text = Files.findOne(???);
console.log(text);
// Meteor.call('slides.insert', text);
return text;
}
})
});
There is a special layer for storing files as chunks, called GridFs because a normal collection will get into problems when you try to store binaries larger than 16MB.
There is also a well established wrapper for handling files in Meteor. Check out the following package: https://github.com/VeliovGroup/Meteor-Files

Proper Way To Ensure All Save Methods Work: node.js, MongoDB

I am quite new to MongoDB and Node.JS, and I have a question about what is the proper way to implement multiple save calls in the same route and ensure that they all work, or none do. What I want, is that if one save fails, they both do. Here is some code. It works as expected with no problems, but I have a feeling that it is bad practice.
My Post Schema, as you can see, it is just some text, and then an array of pointers to another object called Comment.
var postSchema = mongoose.Schema({
text: String,
comments : [{
type : mongoose.Schema.Types.ObjectId,
ref : "Comment"
}]
});
My Comment Schema. All it is, is just some text.
var commentSchema = mongoose.Schema({
commentText: String
});
Now, here is my route...
apiRoutes.post("/testCreateComment", function(request, response, next){
var commentText = request.body.commentText;
var testID = request.body.testID
if (!commentText || !testID) //ensures all data is there.
return next(new Error("Not all the information required was provided."));
var newComment = new Comment({
commentText : commentText
})
newComment.save(function(err) { //first save the new TestComment.
if (err) return next(err);
Post.findById(testID, function(err, found){ //second find the Post object.
if (err) return next(err);
found.comments.push(newComment)
found.save(function(err){ //third, save the Post object with the comment added to the array.
if (err) return next(err);
response.json(newComment.getReturn()); // returns some data to be sent
})
})
})
})
In this route, there are three methods that must be successful for the entire operation to work. Now, for instance, save that the first save works, but for whatever reason the findById is unsuccessful. In this circumstance, I would have a comment floating around in the database that is useless.
My question is, what is the proper way to ensure that all three operations are successful and ensure that the problem described above cannot possibly happen?

Categories