标签:node.js 上传 node.js gridfs kendo-upload
最近又当上了Master,负责带项目,有时候,遇到的问题我很郁闷。比如一个Story,需求中说的是将单个修改改为批量修改,举个例子,商品信息修改,之前是用一个商品id修改,但是现在改成多个商品id修改。我的意思是直接将文本框宽度高度加大,支持回车换行就行了,然后再将API修改为支持批量查询。这个界面上上面是一个Grid,下面是一个表单,选择Grid的数据后,会加载到下面表单。只能加载一条下去,就因为这个,有人提出如果加载一个下去,那么大个文本框只显示一个选中的商品id,视觉上无法接受。说是要将选择的单个和用于输入的多个文本框分开,控制一下显示隐藏的逻辑。这种做法会引来许多问题,大小文本框的隐藏显示会导致页面跳动,如果Grid查询无数据,初始化加载无数据,都要让大文本框显示。如果查询有数据,则grid数据会选中第一条,要显示小文本框。就因为这个问题,我感觉到做个Master真的是不容易,有时候就因为这些小问题达不成一致,让我很恼火。做管理不容易,我还要积累经验。
今天的话,我们来看一下图书管理系统的图书查询,首先我们先录入一批图书信息。
数据准备完成后,我们看一下页面代码。
#book_retrieve(ng-controller=‘bookRetrieveCtrl‘)
.row
.col-md-4
label Book Name:
span.k-textbox.k-space-right(style=‘margin-left:5px;width:70%‘)
input(type=‘text‘ ng-keydown=‘getBook($event)‘ ng-model=‘Search.BookName‘)
a.k-icon.k-i-search(href="javascript:void(0)" ng-click=‘getBook()‘)
.col-md-4
label ISBN:
span.k-textbox.k-space-right(style=‘margin-left:5px;width:70%‘)
input(type=‘text‘ ng-keydown=‘getBook($event)‘ ng-model=‘Search.ISBN‘)
a.k-icon.k-i-search(href=‘javascript:void(0)‘ ng-click=‘getBook()‘)
hr.panel-line
kendo-grid(options=‘bookGridOptions‘ k-data-source=‘BookList‘)
div(kendo-window=‘modals‘ k-width=‘500‘ k-modal=‘true‘ k-visible=‘false‘ k-title=‘"Book Image Upload"‘ k-on-close=‘uploadClose()‘)
.row.row-margin
.col-md-11
kendo-upload(type=‘file‘ name=‘files‘ k-multiple=‘false‘ k-success=‘onUploadSuccess‘ k-upload=‘upload‘ k-error=‘onUploadError‘ k-async=‘{saveUrl:"/book/upload", autoUpload: false}‘)
block scripts
script(type=‘text/javascript‘ src=‘/javascripts/local/book/bookRetrieve.js‘)由于51cto使用的百度的这个富文本编辑器不支持jade语法的高亮显示,所以我把图贴进来。
两个查询条件,BookName,ISBN,支持回车查询。
然后绑定数据,我们使用kendo-grid,绑定的datasource是BookList。然后我们定义了一个弹出页,用kendo-upload来上传图片,上传图片的路径为/book/upload。
ok,接着我们看一下js部分。
var appModule = angular.module(‘bookRetrieveModule‘, ["kendo.directives"]);
appModule.config(function ($locationProvider) {
$locationProvider.html5Mode(true);
});
appModule.controller(‘bookRetrieveCtrl‘, function ($scope, $http, $location) {
Messenger.options = {
extraClasses: ‘messenger-fixed messenger-on-top messenger-on-center‘,
theme: ‘flat‘
}
$scope.showMsg = function (type, msg) {
Messenger().post({
message: msg,
type: type,
hideAfter: 2,
showCloseButton: true
});
}
$scope.BookId = ‘‘;
$scope.Search = {};
$scope.uploadWindow = {
open: function () {
$scope.modals.center().open();
}
};
$scope.bookGridOptions = {
height: 700,
sortable: true,
pageable: {
refresh: true,
pageSizes: [10, 20, 50, 100],
buttonCount: 5
},
resizable: true,
selectable: "single",
columns: [{
template: "<div class=‘center-align-text‘>" +
"<a href=‘/book/image/#=Image#‘><img src=‘/book/image/#=Image#‘ class=‘img-inline‘/></a></div>" ,
field: "Image",
title: "Image",
width: 130
}, {
field: "Title",
title: "Title"
}, {
field: "Author",
title: "Author"
}, {
field: "Price",
title: "Price",
width: 60,
}, {
field: "ISBN",
title: "ISBN"
}, {
field: "Press",
title: "Press"
}, {
command: [
{
name: "Upload",
text: "Upload",
imageClass: ‘k-icon k-insertImage‘,
click: function (e) {
var dataItem = this.dataItem($(e.currentTarget).closest("tr"));
$scope.BookId = dataItem._id;
$scope.uploadWindow.open();
}
}],
width: 150,
}], dataBound: function (rowBoundEvent) {
$("div.center-align-text a").popImage();
}
}
$scope.getBook = function (event) {
if (!event || event.keyCode == 13) {
$scope.BookList = new kendo.data.DataSource({
"pageSize": 15,
"serverPaging": true,
transport: {
read: function (e) {
var url = ‘/book?pageIndex=‘ + (e.data.page-1) + ‘&pageSize=‘ + e.data.pageSize;
if ($scope.Search.BookName) {
url += ‘?bookName=‘ + $scope.Search.BookName
}
if ($scope.Search.ISBN) {
url += ‘&ISBN=‘ + $scope.Search.ISBN;
}
$http.get(url).success(function (data) {
e.success(data);
});
}
},
schema: {
data: function (dataset) {
return dataset.books || [];
},
total: function (dataset) {
return dataset.totalCount || 0;
}
}
});
}
}
$scope.onUploadError = function (error) {
$scope.showMsg(‘error‘, angular.fromJson(error.XMLHttpRequest.responseText).error);
}
$scope.onUploadSuccess = function (e) {
var response = e.response;
if (response.isSuc) {
$scope.showMsg(‘success‘, ‘Upload completed successfully!‘);
}
else {
$scope.showMsg(‘error‘, response.msg);
}
}
$scope.upload = function (e) {
e.sender.options.async.saveUrl = "/book/upload?bookId=" + $scope.BookId;
}
});
angular.element(‘#book_retrieve‘).data(‘$injector‘, ‘‘);
angular.bootstrap(angular.element(‘#book_retrieve‘), [‘bookRetrieveModule‘]);首先我们初始化一个messager,然后$scope.bookId用来记录要上传的图书的id,$scope.Search用来绑定两个查询条件。接着$scope.uploadWindow来初始化一个modal页,用于弹出上传图片modal页(div(kendo-window=‘modals‘)。接着我们定义了kendoGrid,注意这里的Command,拿到当前行绑定的id,然后赋给$scope.BookId,再弹出上传modal页。
接下来是dataBound事件,即每绑定完成一行,就会触发这个事件,这里我们将div下所有的超链接让他支持弹出图片预览。
接下来的$scop.GetBook就是调用api查询了,没什么可说的。下面处理图片上传的回调方法也没什么好说的。
OK,我们接下来看一下服务端。
router.get(‘/book‘, bookRoutes.getBookList);
exports.getBookList = function (req, res) {
var bookName = req.query.bookName;
var ISBN = req.query.ISBN;
var pageIndex = req.query.pageIndex;
var pageSize = req.query.pageSize;
var query = bookModel.find({});
if (bookName) {
query = query.where({ ‘Title‘: { "$regex": bookName, "$options": "i" } });
}
if (ISBN) {
var regexp = new RegExp("^" + ISBN);
query = query.where({ ‘ISBN‘: regexp });
}
query.count().exec(function (error, count) {
query.limit(pageSize)
.skip(pageIndex * pageSize)
.sort(‘-PressDate‘)
.exec(‘find‘, function (error, doc) {
res.json({ books: doc, totalCount: count });
});
});
}按两个条件可以模糊查询。
无条件查询
注意这里的暂无图片,如果/book/image/#=Image#能取到,则显示,否则显示默认图片。
router.get(‘/book/image/:id‘, bookRoutes.getBookImageById);
var fs = require(‘fs‘);
var Grid = require(‘gridfs-stream‘);
var gfs = new Grid(mongoose.connection.db, mongoose.mongo);
exports.getBookImageById = function (req, res) {
gfs.exist({ _id: req.params.id }, function (error, exists) {
if (!exists) {
var rstream = fs.createReadStream(‘./public/images/noimage.jpg‘);
rstream.pipe(res);
}
else {
var readstream = gfs.createReadStream({
_id: new mongoose.Types.ObjectId(req.params.id)
});
readstream.on(‘error‘, function (err) {
console.log(err);
res.send(500, err);
});
readstream.pipe(res);
}
})
}在这里,大家应该还记得上篇文章中提到的book model的定义,Image是一个ObjectId,其实就是GridFs中存储的图片的id。所以在这里读取的时候,只需要传id,就会查出图片并向客户端输出文件流。
OK,最后我们看一下上传
var bookSchemas = require(‘../model/bookinfo.js‘);
var bookMsgRes = require(‘../framework/message/book_msg.js‘);
var validator = require(‘validator‘);
var fs = require(‘fs‘);
var Busboy = require(‘busboy‘);
var mongoose = require(‘mongoose‘);
var Grid = require(‘gridfs-stream‘);
var gfs = new Grid(mongoose.connection.db, mongoose.mongo);
exports.fileupload = function (req, res, next) {
if (!/multipart\/form-data/i.test(req.headers[‘content-type‘])) {
return res.status(500).end(‘Wrong content type‘);
}
var busboy = new Busboy({
headers: req.headers, limits: {
files: 1,
fileSize: 1024 * 1024 * 2
}
});
busboy.on(‘file‘, function (fieldname, file, filename, encoding, mimetype) {
if (mimetype != ‘image/jpeg‘
&& mimetype != ‘image/png‘
&& mimetype != ‘image/bmp‘) {
res.status(403).json({ isSuc: false, error: ‘Can\‘t upload(‘ + filename + ‘) if file\‘s type is not image(jpg,png,bmp)!‘ });
return;
}
var fileId = new mongoose.Types.ObjectId();
var readstream = gfs.createWriteStream({
_id: fileId,
mode: ‘w‘,
content_type: mimetype,
filename: filename,
metadata: {
uploaddate: Date.now()
}
});
file.pipe(readstream);
bookModel.findByIdAndUpdate(req.query.bookId, { $set: { Image: fileId } }, function (error, doc) { });
});
busboy.on(‘finish‘, function () {
res.json({ isSuc: true });
});
busboy.on(‘error‘, function (err) {
res.status(500).end({ isSuc: false, msg: err });
});
req.pipe(busboy);
};在这里我们需要使用busBoy上传图片把并存储至gridfs,上传成功的话,修改book的Image字段。
上传失败,如下
如果成功,如下
OK,最后我们看一下整体效果
OK,到此的话,图书查询就全部结束了,下节我们继续看图书Gallery。
本文出自 “技术创造价值” 博客,请务必保留此出处http://leelei.blog.51cto.com/856755/1790907
标签:node.js 上传 node.js gridfs kendo-upload
原文地址:http://leelei.blog.51cto.com/856755/1790907