web-dev-qa-db-ja.com

LoopBackでメタデータを含むファイルを保存する方法

私がやりたいこと:内部にファイルが入力されたhtmlフォームを作成します。ファイルが選択されると、ファイル入力はファイルをアップロードし、ファイルIDを取得する必要があります。したがって、フォームが送信されると、ファイルIDがフォームとともに投稿され、データベースに書き込まれます。

短いバージョン:メタデータ(idなど)をファイルに保存したい

シンプルに聞こえますが、LoopBackでそれを行うのに苦労しています。

このトピックについていくつかの会話( 12 )がありましたが、どちらも解決につながるようには見えなかったので、私はこれを一度見つけるのに良い場所だと思いました。

最も単純な解決策はモデルの関係を使用することですが、LoopBackはファイルストレージサービスとの関係をサポートしていません。バンプ。したがって、たとえばFileという名前の永続モデルを使用し、デフォルトのcreate、deleteをオーバーライドして、Storageという名前のファイルストアモデルを保存および削除する必要があります。

これまでの私のセットアップ:

  • ループバックストレージサービス に接続され、ローカルファイルシステムにファイルを正常に保存しているモデル/ api/Storageがあります。
  • ファイルメタデータを使用してMongoに接続されたPersistedModelがあります:namesizeurlおよびobjectId
  • ファイルを最初に保存してからcreateFile.create()に挿入できるように、リモートフックをbefore urlにセットアップしています

私はそこにいます、そして このLoopBackページ によると、私は内部にファイルを持つべきctxを持っています:

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

ctxとは

ctx.req:Express Requestオブジェクト。
ctx.result:Express Responseオブジェクト。

さて、今私はExpressページにいますが、かなり迷っており、「ボディ解析ミドルウェア」について何か言っていますが、それが何であるかはわかりません。

私は解決策に近づいているように感じます、助けをいただければ幸いです。このアプローチは正しいですか?

47
Mihaly KR

ループバックのファイルにメタデータを保存するためのfullソリューションを次に示します。

コンテナモデルが必要です

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": []
}

そして、filescontainerに接続します:

common/models/files.js

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モデルに追加するAPIファイルを公開するには、正しいデータソースを選択してください。

...
"files": {
    "dataSource": "db",
    "public": true
}
...

完了!fileフォームフィールドのファイルバイナリデータを使用して、POST /api/files/upload]を呼び出すことができます。名前、タイプ、URLを返します。

56
Mihaly KR

同じ問題がありました。メタデータとアップロード方法を保存する独自のモデルを作成することで解決しました。

  1. Name、type、url、userIdなどの情報を保存するモデルFile(あなたと同じ)を作成しました

  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'}
      }
    
    );
    
10
Harshil Lodhi

質問「ファイルをアップロードする前にファイル形式をチェックする方法」への回答を探している人向け。

この場合、実際にはオプションのパラメーターallowedContentTypesを使用できます。

ディレクトリでbootサンプルコードを使用:

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

私はそれが誰かを助けることを願っています。

8
av-k

シナリオによっては、署名などを利用して、Amazon S3、TransloadIT(画像処理用)、または同様のサービスに直接アップロードできるようにすることを検討する価値があります。

このコンセプトに関する最初の決定は、GraphQLを使用しているため、GraphQLを介したマルチパートフォームのアップロードを回避したいということでした。さらに、リソースを(大規模な)アップロードおよび関連するファイルの検証と処理で拘束することなく、これらのサーバーの効率を維持したかったのです。

ワークフローは次のようになります。

  1. データベースレコードを作成する
  2. レコードIDとファイルアップロード署名データを返します(S3バケットまたはTransloadITエンドポイント、および認証トークンを含む)
  3. クライアントがエンドポイントにアップロードする

バナーやアバターのアップロードなどを行う場合、ステップ1はすでに存在するため、そのステップはスキップします。

さらに、S3バケットにSNSまたはSQS通知を追加して、関連するオブジェクトにファイルが添付されたことをデータベースで確認できます-効果的にステップ4。

これは複数のステップからなるプロセスですが、コアAPI内でファイルのアップロードを処理する必要性を排除してうまく機能します。これまでのところ、これはユーザーアバターやレコードへのPDFの添付などの初期実装(このプロジェクトの初期)からうまく機能しています。

参照例:

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

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

1
Mark Johnson

loopback 3 and Postmanでその問題を抱えている他の人のために、POSTで接続がハングする(またはERR_EMPTY_RESPONSEを返す)(ここのコメントで見られる)...このシナリオの問題は、Postmanが使用することですContent-Type "application/x-www-form-urlencoded"として!

そのヘッダーを削除して、「Accept」=「multipart/form-data」を追加してください。この動作のループバックでバグをすでに提出しています

1
PArt

AngularJS SDKユーザーの場合... Container.upload()などの生成されたメソッドを使用する場合は、lb-services.jsを使用して、Content-Typeヘッダーをundefinedに設定します。これにより、クライアントはContent-Typeヘッダーを設定し、境界値を自動的に追加できます。次のようになります。

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

データを"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();
})
0
Robins Gupta