如何在LoopBack中存储带有元数据的文件?

时间:2015-03-05 18:39:29

标签: loopbackjs strongloop

我想做什么: 有一个html表单,里面有文件输入。选择文件时,文件输入应上传文件,并获取文件ID,因此在提交表单时,文件ID将与表单一起发布并写入数据库。

更短的版本:我想用我的文件存储元数据(例如id)。

听起来很简单,但我很难在LoopBack中做到这一点。

关于这个主题已经进行了几次对话(12),似乎都没有找到解决方案,所以我认为这可能是找到一次和所有

最简单的解决方案是使用模型关系,但LoopBack不支持与文件存储服务的关系。凹凸。所以我们必须使用名为File的persistedmodel,并覆盖默认的create,delete,以便保存和删除我所拥有的文件存储模型 - 名为Storage

到目前为止我的设置:

  • 我有一个模型/ api / Storage连接到loopback storage service并将文件成功保存到本地文件系统。
  • 我有一个与Mongo相关联的PersistedModel文件元数据:namesizeurlobjectId
  • 我在create之前设置了一个远程钩子,因此可以先保存文件,然后将url注入File.create()

我在那里,根据this LoopBack page,我有ctx,里面应该有文件:

File.beforeRemote('create', function(ctx, affectedModelInstance, next) {})`

什么是ctx

  

ctx.req:快速请求对象   ctx.result:快速响应对象。

好的,所以现在我在Express页面上,非常迷失,这就是一个关于“身体解析中间件”的东西,我不知道它可能是什么。

我觉得我接近解决方案,任何帮助都会受到赞赏。这种方法对吗?

7 个答案:

答案 0 :(得分:56)

这里是完整的解决方案,用于存储带有环回文件的元数据。

您需要一个容器模型

ALTER proc [dbo].[spMySP] @Object nvarchar(20), -- comments @Type nchar(1), -- comments @num_of_cols_in_key int, @listCols nvarchar(255), @ListColdel nvarchar(255) AS begin -- Return values

common/models/container.json

{ "name": "container", "base": "Model", "idInjection": true, "options": { "validateUpsert": true }, "properties": {}, "validations": [], "relations": {}, "acls": [], "methods": [] } 中为容器创建数据源。例如:

server/datasources.json

您需要在... "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/files.json

现在将{ "name": "files", "base": "PersistedModel", "idInjection": true, "options": { "validateUpsert": true }, "properties": { "name": { "type": "string" }, "type": { "type": "string" }, "url": { "type": "string", "required": true } }, "validations": [], "relations": {}, "acls": [], "methods": [] } files

联系起来

container

common/models/files.js

为了公开文件api,将var CONTAINERS_URL = '/api/containers/'; module.exports = function(Files) { Files.upload = function (ctx,options,cb) { if(!options) options = {}; ctx.req.params.container = 'common'; Files.app.models.container.upload(ctx.req,ctx.result,options,function (err,fileObj) { if(err) { cb(err); } else { var fileInfo = fileObj.files.file[0]; Files.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); } }); } }); }; Files.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'} } ); }; 文件添加到model-config.json模型,请记住选择正确的数据源:

files

完成!现在,您可以在... "files": { "dataSource": "db", "public": true } ... 表单字段中使用文件二进制数据调用POST /api/files/upload。您将获得ID,姓名,类型和网址作为回报。

答案 1 :(得分:10)

我遇到了同样的问题。我通过创建自己的模型来存储元数据和我自己的上传方法来解决它。

  1. 我创建了一个模型File,它将存储名称,类型,网址,userId(与您的相同)等信息

  2. 我创建了自己的上传远程方法,因为我无法使用钩子。容器模型是由loopback-component-storage创建的模型。

  3. var fileInfo = fileObj.files.myFile[0];此处myFile是文件上传的字段名称,因此您必须相应地更改它。如果您没有指定任何字段,那么它将显示为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'}
      }
    
    );
    

答案 2 :(得分:8)

对于那些正在寻找问题答案的人"如何在上传文件之前检查文件格式"

在这种情况下,我们可以使用可选参数 allowedContentTypes

  

在目录 boot 中使用示例代码:

module.exports = function(server) {
    server.dataSources.filestorage.connector.allowedContentTypes = ["image/jpg", "image/jpeg", "image/png"];
}

我希望它会帮助别人。

答案 3 :(得分:1)

根据您的情况,可能值得考虑使用签名或类似功能,允许直接上传到Amazon S3,TransloadIT(用于图像处理)或类似服务。

我们对这个概念的第一个决定是,当我们使用GraphQL时,我们希望通过GraphQL避免多部分表单上传,而GraphQL又需要转移到我们后面的Loopback服务。此外,我们希望保持这些服务器的高效率,而不会通过(大)上传和相关文件验证和处理来占用资源。

您的工作流程可能如下所示:

  1. 创建数据库记录
  2. 返回记录ID和文件上传签名数据(包括S3存储桶或TransloadIT端点,以及任何身份验证令牌)
  3. 客户端上传到终端
  4. 对于横幅或头像上传等操作的情况,步骤1已经存在,因此我们跳过了这一步。

    此外,您可以将SNS或SQS通知添加到S3存储桶,以在数据库中确认相关对象现在已附加文件 - 实际上是步骤4。

    这是一个多步骤的过程,但可以很好地消除在核心API中处理文件上传的需要。到目前为止,这对于用户头像和将PDF附加到记录等内容的初始实现(此项目的早期阶段)运行良好。

    示例参考:

    http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html

    https://transloadit.com/docs/#authentication

答案 4 :(得分:1)

对于其他任何遇到 loopback 3和Postman 问题的人,在POST时,连接会挂起(或返回ERR_EMPTY_RESPONSE)(在此处的某些评论中看到)...此方案中的问题是, Postman使用的内容类型为“application / x-www-form-urlencoded”!

请删除该标题并添加“Accept”=“multipart / form-data”。我已经在环回中为此行为提交了一个错误

答案 5 :(得分:0)

只需将数据作为"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();
})

答案 6 :(得分:0)

对于AngularJS SDK用户...如果您想使用像Container.upload()这样的生成方法,您可能需要添加一行来配置lb-services.js中的方法以将Content-Type标头设置为undefined。这将允许客户端设置Content-Type标头并自动添加边界值。看起来像这样:

 "upload": {
    url: urlBase + "/containers/:container/upload",
    method: "POST",
    headers: {"Content-Type": undefined}
 }