首页 > 代码库 > LoopBackJS 之 文件上传下载——使用loopback-component-storage
LoopBackJS 之 文件上传下载——使用loopback-component-storage
参考链接:
http://loopback.io/doc/en/lb2/Storage-component.html#creating-a-storage-component-data-source
http://stackoverflow.com/questions/37238414/validate-and-rename-image-file-from-loopback-component-storage?noredirect=1
http://stackoverflow.com/questions/27743424/strongloop-file-upload-inside-model?noredirect=1
一、简介:
(翻译自官方文档的Overview) :)
使用LoopBack storage组件可以很容易的从云存储以及本地文件系统下载或者是上传文件。
在云提供商(包括Amazon、Rackspace、Openstack、Azuer)中该组件提供了Nodejs和REST API 来管理二进制内容,
你可以像使用Loopback data source(比如数据库)一样来使用该存储组件,和其他数据源一样,该组件支持create,read,
update,delete(CRUD)操作,而且也提供了同样的Loopback和REST API。
注意:该组件目前没有提供“开箱即用”的元数据(metadata)管理。
二、安装
安装存储组件和安装普通的Node包一样
1 npm install --save loopback-component-storage
三、容器(Containers)和文件(files)
该存储组件将内容组织成 容器(containers)和 文件(files)。容器内存有文件集合,并且每个文件属于单个容器(container)
容器 将文件分组,类似于目录或者是文件夹。每个容器 定义了对象的命名空间并且使用唯一的名字来区分,通常是在用户账户内。
注意:容器内不能有子容器。
文件 储存数据,例如文档或者是图像。每个文件总是在且仅仅在一个容器 内。在容器内部,每个文件拥有唯一的名字。在不同容器
里的文件可以有相同的名字。(这也就意味着,如果之前容器内已经有了同名文件,后上传的文件会覆盖之前的文件。 文章最后会讲如何处理此问题。)
接下来就是正式的操作过程了。 - -
四、 新建存储组件数据源
$ slc loopback:datasource [?] Enter the data-source name: storage [?] Select the connector for myfile: other [?] Enter the connector name without the loopback-connector- prefix: loopback-component-storage [?] Install storage (Y/n)
这样我们就创建好了一个名为“storage”的数据源了 ,然后我们编辑 /server/datasources.json 文件,手动添加此数据源需要的配置。
这里我们以本地文件系统为例。
"storage": { "name": "storage", "connector": "loopback-component-storage", "provider": "filesystem", "root": "./server/storage" }
其中“root”参数为存储文件的根目录,此处root参数表示 项目根目录下server文件夹内的storage文件夹。
创建好数据源后还需要model来进行容器(container)的操作,下一步:创建model 取名为container
五、新建容器 model
$ slc loopback:model ? Enter the model name: Container ? Select the data-source to attach Container to: storage (loopback-component-st orage) ? Select model‘s base class Model ? Expose Container via the REST API? Yes ? Custom plural form (used to build REST URL): ? Common model or server only? common Let‘s add some Container properties now. Enter an empty property name when done. ? Property name: $
这是生成的model json文件内容:
{ "name": "Container", "base": "Model", "properties": {}, "validations": [], "relations": {}, "acls": [], "methods": {} }
注意:这里base class必须选择为Model,因为此数据源并不是数据库。
当然,我们除了要实现文件的上传和下载外,还需要记录下文件的相关信息,我们还要创建一个model来实现这
部分的逻辑。
下一步:创建文件模型,取名为File。File模型的 property可以选择自己感兴趣的信息,比如
文件类型,文件名称,文件url。
五、新建文件model
$:slc loopback:model ? Enter the model name: File ? Select the data-source to attach file to: local //"local" 是我自己配置好的一个oracle数据库配置 ? Select model‘s base class:PersistedModel
? Expose file via the REST API? Yes
? Custom plural form (used to build REST URL):
? Common model or server only? common
Let‘s add some file properties now. Enter an empty property name when done.
? Property name: type
invoke loopback:property
? Property type: string
? Required? No
? Default value[leave blank for none]:
Let‘s add another file property. Enter an empty property name when done.
? Property name: name
invoke loopback:property
? Property type: string
? Required? No
? Default value[leave blank for none]:
Let‘s add another file property. Enter an empty property name when done. ? Property name: url invoke loopback:property ? Property type: string ? Required? No ? Default value[leave blank for none]:
Let‘s add another file property. Enter an empty property name when done.
? Property name:
这是生成的model json文件内容:
{ "name": "File", "base": "PersistedModel", "idInjection": true, "options": { "validateUpsert": true }, "properties": { "type": { "type": "string" }, "name": { "type": "string" }, "url": { "type": "string" } }, "validations": [], "relations": {}, "acls": [], "methods": {} }
到此,我们需要的model就都创建完毕了。 :)
现在我们要把File model和 Container model联系在一起
在/common/models/file.js 中添加代码
var CONTAINERS_URL = ‘/api/containers/‘;
module.exports = function(File) {
File.upload = function (ctx,options,cb) {
if(!options) options = {};
ctx.req.params.container = ‘common‘; // "common" 为之前数据源中root 参数 /server/storage 目录下的文件夹“common” 需要自己创建好
File.app.models.container.upload(ctx.req,ctx.result,options,function (err,fileObj) { // ctx.req 是express request object ctx.result 是express response object
if(err) {
cb(err);
} else {
var fileInfo = fileObj.files.file[0]; //此处 file和表单中field name相同
File.create({
name: fileInfo.name,
type: fileInfo.type,
url: CONTAINERS_URL+fileInfo.container+‘/download/‘+fileInfo.name
},function (err,obj) {
if (err !== null) {
cb(err);
} else {
cb(null, obj);
}
});
}
});
};
File.remoteMethod(
‘upload‘,
{
description: ‘Uploads a file‘,
accepts: [
{ arg: ‘ctx‘, type: ‘object‘, http: { source:‘context‘ } },
{ arg: ‘options‘, type: ‘object‘, http:{ source: ‘query‘} }
],
returns: {
arg: ‘fileObject‘, type: ‘object‘, root: true
},
http: {verb: ‘post‘}
}
);
};
现在,我们可以通过 POST /api/files/upload 来上传文件了 (注意表单field名为file 与 上述代码中的浅绿色部分相对应, 当然你也可以用其他名字, 只要保证表单中上传文件的field名字 与代码中相对应。)
简单的一个文件上传表单(需要设置enctype 不要漏掉 input标签name值应当为“file”与代码中相对应,不要漏掉),使用jade模板:
doctype html html head meta(charset="utf-8") title 文件上传 body div form(role="form", action=‘/api/files/upload, method="POST", enctype="multipart/form-data") input(type="file", name="file") button(type="submit") 上传文件
这是我上传文件成功后返回的数据:
{ "type": "application/msword", "name": "测试报告(附件6).DOC", "url": "/api/containers/common/download/测试报告(附件6).DOC", "container": "common", "id": 1 }
在实际的文件上传中会发现,当上传同名文件时,会发现后上传的文件会把之前服务器上存在的同名文件给覆盖掉。这种情况怎么解决呢? 一种比较简单的实现方式是给文件加上时间戳。
还有,可能还需要限制上传文件的大小、类型,这种又如何实现呢?
只要在/server/datasources.local.js 中添加如下代码即可。
module.exports = { storage:{ // "storage" 是之前创建存储组件数据源时取的名字 allowedContentTypes:[‘application/msword‘, ‘image/jpg‘, ‘image/png‘, ‘image/jpeg‘, ‘image/tiff‘], // 限定上传文件的类型 maxFileSize: 50 * 1024 * 1024, // 限定上传文件大小为50M getFilename:function(fileInfo){ var fileName = fileInfo.name.replace(/\s+/g, ‘-‘).toLowerCase(); var dotPosition = fileName.lastIndexOf(‘.‘); var name = fileName.substring(0, dotPosition); var extension = ""; if (dotPosition !== -1){ extension = fileName.substring(dotPosition); } return name + ‘-‘ + Date.now() + extension; //给文件名加上时间戳 } } };
:) 这是添加代码后上传成功返回的数据:
{ "type": "application/msword", "name": "项目安装部署指南-1480669801338.doc", "url": "/api/containers/common/download/项目安装部署指南-1480669801338.doc", "container": "common", "id": 3 }
其中“url”参数值就是文件下载地址了。
LoopBackJS 之 文件上传下载——使用loopback-component-storage