如何在LoopBack中存储具有元数据的文件?
我想做什么:有一个HTML表单,里面有一个文件input。 当select一个文件时,文件input应该上传文件,并获得一个文件ID,所以当表单被提交时,文件ID被张贴在表单上并写入数据库。
较短的版本: 我想用我的文件存储元数据(例如id)。
听起来很简单,但我很难在LoopBack中做到这一点。
关于这个话题已经有了一些对话( 1,2 ),似乎也没有find解决办法,所以我认为这可能是一个永远find一个好地方的好地方。
最简单的解决scheme是使用模型关系,但是LoopBack不支持与文件存储服务的关系。 磕碰。 因此,我们必须使用一个名为File
的持久化模型,并覆盖默认的create,delete,以便从文件存储模型中保存和删除 – 名为Storage
。
我的设置到目前为止:
- 我有一个模型/ API /存储连接到环回存储服务,并成功地将文件保存到本地文件系统。
- 我有一个PersistedModel连接到Mongo与文件元数据:
name
,size
,url
和objectId
- 我
create
之前设置了一个远程钩子,因此可以先保存文件,然后将url
注入到File.create()
我在那里,根据这个LoopBack页面 ,我有应该在里面的文件的ctx:
File.beforeRemote('create', function(ctx, affectedModelInstance, next) {})`
什么是ctx
?
ctx.req
:快速请求对象。
ctx.result
:快速响应对象。
好的,所以现在我在Express页面,很迷茫,这是一个关于“身体parsing中间件”的东西,我不知道它可能是什么。
我觉得我接近解决scheme, 任何帮助将不胜感激 。 这种方法是对的吗?
以下是以回送方式存储元数据和文件的完整解决scheme。
你需要一个容器模型
common/models/container.json
{ "name": "container", "base": "Model", "idInjection": true, "options": { "validateUpsert": true }, "properties": {}, "validations": [], "relations": {}, "acls": [], "methods": [] }
在server/datasources.json
为您的容器创build数据源。 例如:
... "storage": { "name": "storage", "connector": "loopback-component-storage", "provider": "filesystem", "root": "/var/www/storage", "maxFileSize": "52428800" } ...
您将需要在server/model-config.json
中将此模型的数据源设置为您具有的loopback-component-storage
:
... "container": { "dataSource": "storage", "public": true } ...
您还需要一个文件模型来存储元数据并处理容器调用:
common/models/file.json
{ "name": "file", "base": "PersistedModel", "idInjection": true, "options": { "validateUpsert": true }, "properties": { "name": { "type": "string" }, "type": { "type": "string" }, "url": { "type": "string", "required": true } }, "validations": [], "relations": {}, "acls": [], "methods": [] }
现在连接file
与container
:
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'; File.app.models.container.upload(ctx.req,ctx.result,options,function (err,fileObj) { if(err) { cb(err); } else { var fileInfo = fileObj.files.file[0]; File.create({ name: fileInfo.name, type: fileInfo.type, container: fileInfo.container, 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'} } ); };
完成! 您现在可以在file
表单字段中使用文件二进制数据调用POST /api/file/upload
。 您将返回id,名称,types和url。
我有同样的问题。 我通过创build自己的模型来存储元数据和我自己的上传方法来解决这个问题。
-
我创build了一个模型
File
,它将存储像名称,types,url,userId(与你的相同) -
我创build了我自己的上传远程方法,因为我无法做到这一点的钩子。 容器模型是由回送组件存储创build的模型。
-
var fileInfo = fileObj.files.myFile[0];
这里myFile是file upload的字段名,所以你必须相应地改变它。 如果你没有指定任何字段,那么它将作为fileObj.file.null[0]
。 这段代码没有正确的错误检查,在生产环境中部署之前就做好了 。File.uploadFile = function (ctx,options,cb) { File.app.models.container.upload(ctx.req,ctx.result,options,function (err,fileObj) { if(err) cb(err); else{ // Here myFile is the field name associated with upload. You should change it to something else if you var fileInfo = fileObj.files.myFile[0]; File.create({ name: fileInfo.name, type: fileInfo.type, container: fileInfo.container, userId: ctx.req.accessToken.userId, url: CONTAINERS_URL+fileInfo.container+'/download/'+fileInfo.name // This is a hack for creating links },function (err,obj) { if(err){ console.log('Error in uploading' + err); cb(err); } else{ cb(null,obj); } }); } }); }; File.remoteMethod( 'uploadFile', { 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'} } );
对于那些正在寻找“如何在上传文件之前检查文件格式”这个问题的答案的人。
在这种情况下,我们可以使用可选参数allowedContentTypes 。
在目录引导中使用示例代码:
module.exports = function(server) { server.dataSources.filestorage.connector.allowedContentTypes = ["image/jpg", "image/jpeg", "image/png"]; }
我希望这会帮助别人。
只需将数据作为"params"
对象传递,并在服务器上,您可以将其作为ctx.req.query
例如
在客户端
Upload.upload( { url: '/api/containers/container_name/upload', file: file, //Additional data with file params:{ orderId: 1, customerId: 1, otherImageInfo:[] } });
在服务器端
假设您的存储模型名称是container
Container.beforeRemote('upload', function(ctx, modelInstance, next) { //OUPTUTS: {orderId:1, customerId:1, otherImageInfo:[]} console.log(ctx.req.query); next(); })
根据您的情况,可能需要查看使用签名或类似的function,以允许直接上传到Amazon S3,TransloadIT(用于image processing)或类似的服务。
我们对这个概念的第一个决定是,在我们使用GraphQL的时候,我们希望避免通过GraphQL来进行多部分的表单上传,而这反过来又需要转移到它后面的Loopback服务上。 另外,我们希望保持这些服务器的高效率,而不用占用(大量)上传和关联文件validation和处理的资源。
您的工作stream程可能如下所示:
- 创build数据库logging
- 返回loggingID和file upload签名数据(包括S3存储桶或TransloadIT端点,以及任何authentication令牌)
- 客户端上传到端点
对于像横幅或头像上传的情况,步骤1已经存在,所以我们跳过这一步。
另外,您还可以将SNS或SQS通知添加到您的S3存储桶中,以便在数据库中确认相关对象现在已经附加了一个文件 – 实际上是第4步。
这是一个多步骤的过程,但可以很好地解决在核心API中处理file upload的需要。 到目前为止,从我们最初的实施(在这个项目的早期阶段),这个工作就像用户头像以及将PDF附加到logging一样。
参考示例:
http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html
对于任何其他问题的回环3和Postman ,在POST,连接挂起(或返回ERR_EMPTY_RESPONSE)(在这里看到一些注释)…在这种情况下的问题是,该邮递员使用内容types“应用程序/ X WWW的forms,进行了urlencoded“!
请删除该标题并添加“Accept”=“multipart / form-data”。 我已经在这个行为的回送中提交了一个bug
对于AngularJS SDK用户…如果您想要使用像Container.upload()这样的生成方法,您可能需要添加一行来configurationlb-services.js
的方法,将Content-Type头设置为undefined
。 这将允许客户端设置Content-Type标题并自动添加边界值。 会看起来像这样:
"upload": { url: urlBase + "/containers/:container/upload", method: "POST", headers: {"Content-Type": undefined} }