我正在尝试创建一个WebApp,用户可以在其中上传一些图像,为了完成此任务,我使用了gridfs
。
我想出了如何正确保存它们的方法,但是当我必须显示它们时才出现问题。
为保存图像,我使用此代码
保存图像
const mongoURI = "mongodb://localhost:27017/upload_images";
mongoose.set('useNewUrlParser', true);
mongoose.set('useFindAndModify', false);
mongoose.connect(mongoURI);
const conn = mongoose.connection;
// Init gfs
let gfs;
conn.once('open', () => {
// Init stream
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('uploaded_images'); //collection name
});
// Create storage engine
const storage = new GridFsStorage({
url: mongoURI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'uploaded_images' //collection name
};
resolve(fileInfo);
});
});
}
});
const upload = multer({ storage });
router.post('/posts', upload.single('file'), (req, res) => {
file = req.file;
if(file){
req.body.post.image_file = file.id;
}
Post.create(req.body.post, (err, post) => {
if(err){
res.redirect('/posts');
} else {
res.redirect('/posts');
}
});
});
并且图像已正确保存,现在我必须显示它们。
我有一个mongoose
发布模式,在其中保存了一些数据和上载图像的引用(ObjectId)
发布架构
const mongoose = require('mongoose');
var PostSchema = new mongoose.Schema({
image_link: String,
image_file: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'GFS'
}],
brand: String,
model: String,
moto_type: String,
date : {type : Date, default : Date.now},
categories : [{
title: String,
content: String,
}],
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}],
views: {type: String, default: '0'}
});
module.exports = mongoose.model('Post', PostSchema, 'posts');
还有一个postf模式内使用引用的gridfs模式
GridFs模式
const mongoose = require('mongoose');
var gridFsSchema = new mongoose.Schema({});
module.exports = mongoose.model('GFS', gridFsSchema, 'uploaded_images.files');
当我尝试显示帖子时,我的问题开始了
router.get('/posts/:post_id', (req, res) => {
post_id = req.params.post_id;
Post.findById(post_id)
.populate('comments')
.populate('image_file')
.exec((err, post) => {
if(err){
console.log(err)
res.redirect('/posts');
} else {
res.render('posts/show', {post: post})
}
});
});
第一个问题是.populate('image_file')
方法抛出此错误
{ MissingSchemaError: Schema hasn't been registered for model "GFS".
Use mongoose.model(name, schema)
at new MissingSchemaError (/home/pero/motoWebApp/node_modules/mongoose/lib/error/missingSchema.js:22:11)
at NativeConnection.Connection.model (/home/pero/motoWebApp/node_modules/mongoose/lib/connection.js:925:11)
at getModelsMapForPopulate (/home/pero/motoWebApp/node_modules/mongoose/lib/model.js:4434:59)
at populate (/home/pero/motoWebApp/node_modules/mongoose/lib/model.js:3979:21)
at _populate (/home/pero/motoWebApp/node_modules/mongoose/lib/model.js:3949:5)
at utils.promiseOrCallback.cb (/home/pero/motoWebApp/node_modules/mongoose/lib/model.js:3924:5)
at Object.promiseOrCallback (/home/pero/motoWebApp/node_modules/mongoose/lib/utils.js:249:12)
at Function.Model.populate (/home/pero/motoWebApp/node_modules/mongoose/lib/model.js:3923:16)
at model.Query.Query._completeOne (/home/pero/motoWebApp/node_modules/mongoose/lib/query.js:2018:9)
at Immediate.Query.base.findOne.call (/home/pero/motoWebApp/node_modules/mongoose/lib/query.js:2057:10)
at Immediate.<anonymous> (/home/pero/motoWebApp/node_modules/mquery/lib/utils.js:116:16)
at runCallback (timers.js:705:18)
at tryOnImmediate (timers.js:676:5)
at processImmediate (timers.js:658:5)
message:
'Schema hasn\'t been registered for model "GFS".\nUse mongoose.model(name, schema)',
name: 'MissingSchemaError' }
第二个是我向/posts/:post_id
发出获取请求时不理解如何显示图像
答案 0 :(得分:0)
我已经解决了这个问题:
为了使用gridfs正确显示图像,您必须执行以下步骤:
1)
上载文件时,将ID作为参考保存在Schema中,以便以后可以用find_words
填充它。您可以通过在模式中插入以下行来将其保存为参考
.populate method
2)
创建一个grifs模型用作上传文件的参考
image_file: {
type: mongoose.Schema.Types.ObjectId,
ref: 'GridFs'
},
我在问题中遇到的错误是因为我在外部文件中定义了模型而从未导入过该错误而引发的。
3)
定义渲染文件的路径:
const mongoose = require('mongoose');
var GridfsSchema = new mongoose.Schema({
filename: String
}, {strict: false});
module.exports = mongoose.model('GridFs', GridfsSchema, 'uploaded_images.files' );
现在,每次您进入路径
const express = require('express'),
router = express.Router(),
mongoose = require('mongoose'),
path = require('path'),
multer = require('multer'),
crypto = require('crypto'),
Grid = require('gridfs-stream'),
GridFsStorage = require('multer-gridfs-storage'),
Post = require('../models/post');
const conn = mongoose.connection;
const mongoURI = "mongodb://localhost:27017/moto_website";
// Init gfs
let gfs;
conn.once('open', () => {
// Init stream
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('uploaded_images'); //collection name
});
router.get('/image/:filename', (req, res) => {
gfs.files.findOne({filename: req.params.filename}, (err, file) => {
if(!file || file.length === 0){
return res.status(404).json({err: 'No File Exists'});
} else {
// Check if is image
if(file.contentType === "image/jpeg" || file.contentType === "image/png"){
// Read output to broswer
const readstream = gfs.createReadStream(file.filename);
readstream.pipe(res);
} else {
res.status(404).json({err: 'Not and image'});
}
}
});
});
时,它都会渲染名称为/images/:image_name
的图像
4)
设置为:image_name
的{{1}}属性会标记路径src
来源
在找到解决方案之前我遇到了一些错误
1)我可以检索填充的对象,但不能检索给我未定义的对象的属性。这是因为在gridfs Schema中,我有一个空对象,所以我什么也找不到。通过在<img>
中定义属性/images/<your_filename_in_gridfs.files>
,我解决了该问题(仅对于filename属性,其他属性将返回undefined。如果您也要读取它们,则必须将它们写入{{1 }})。参见This answer
2)我不知道如何将gridfs文件链接到相对引用See this answer
答案 1 :(得分:0)
我知道这已经很晚了,但这是为了帮助任何想要更新解决方案的人。由于将不推荐使用旧版Grid系统,因此我只是分享对我有用的东西。您可以在MongoDB官方文档中阅读更多内容。
import express from "express";
import mongoDB from "mongodb";
import assert from "assert";
const dbName = "Your Database"
const app = express();
app.use(express.json());
app.get('/image/:filename', (req, res) => {
mongoDB.MongoClient.connect("Your Mongo Uri",
{
useNewUrlParser:true,
useUnifiedTopology:true
},
(error,client) => {
assert.ifError(error)
const db = client.db(dbName);
var bucket = new mongoDB.GridFSBucket(db);
bucket.openDownloadStreamByName(req.params.filename)
.pipe(res)
});
});
app.listen(4000, () => (console.log("Server Started on port 4000")));