1. ホーム
  2. node.js

NodeJSでAmazon S3へファイルをアップロードする

2023-09-13 08:31:57

質問

S3 バケットにファイルをアップロードしようとしているときに問題にぶつかりました。私のファイル パラメータが適切でないように見えることを除いて、すべてが動作します。私は、nodejsからS3へのアップロードにAmazon S3 sdkを使用しています。

これらは私のルート設定です。

var multiparty = require('connect-multiparty'),
    multipartyMiddleware = multiparty();
app.route('/api/items/upload').post(multipartyMiddleware, items.upload);

items.upload()関数です。

exports.upload = function(req, res) {
    var file = req.files.file;
    var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
    s3bucket.createBucket(function() {
        var params = {
            Key: file.name,
            Body: file
        };
        s3bucket.upload(params, function(err, data) {
            console.log("PRINT FILE:", file);
            if (err) {
                console.log('ERROR MSG: ', err);
            } else {
                console.log('Successfully uploaded data');
            }
        });
    });
};

設定 Body のような文字列を "hello" は正常に動作します。によると doc , Body param は (Buffer, Typed Array, Blob, String, ReadableStream) オブジェクトデータを取ります。 しかし、ファイルオブジェクトのアップロードは以下のエラーメッセージが表示され、失敗します。

[Error: Unsupported body payload object]

これはファイルオブジェクトです。

{ fieldName: 'file',
  originalFilename: 'second_fnp.png',
  path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
  headers: 
   { 'content-disposition': 'form-data; name="file"; filename="second_fnp.png"',
     'content-type': 'image/png' },
  ws: 
   { _writableState: 
      { highWaterMark: 16384,
        objectMode: false,
        needDrain: true,
        ending: true,
        ended: true,
        finished: true,
        decodeStrings: true,
        defaultEncoding: 'utf8',
        length: 0,
        writing: false,
        sync: false,
        bufferProcessing: false,
        onwrite: [Function],
        writecb: null,
        writelen: 0,
        buffer: [],
        errorEmitted: false },
     writable: true,
     domain: null,
     _events: { error: [Object], close: [Object] },
     _maxListeners: 10,
     path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
     fd: null,
     flags: 'w',
     mode: 438,
     start: undefined,
     pos: undefined,
     bytesWritten: 261937,
     closed: true },
  size: 261937,
  name: 'second_fnp.png',
  type: 'image/png' }

どんな助けでも大歓迎です!

どのように解決するのですか?

ここでは、いくつかのことがうまくいっていないようです。あなたの投稿に基づくと、ファイル アップロードをサポートしようとしているように見えます。 connect-multiparty ミドルウェアを使用しています。このミドルウェアが行うことは、アップロードされたファイルを受け取り、それをローカルのファイルシステムに書き込んで、それから req.files をアップロードされたファイルに設定します。

ルートの設定は問題ないようです。 items.upload() 関数にあるようです。特にこの部分です。

var params = {
  Key: file.name,
  Body: file
};

回答の冒頭で述べたように connect-multiparty はローカルファイルシステムにファイルを書き込むので、ファイルを開いて読み、アップロードし、ローカルファイルシステムで削除する必要があります。

つまり、次のようなメソッドに更新することができます。

var fs = require('fs');
exports.upload = function (req, res) {
    var file = req.files.file;
    fs.readFile(file.path, function (err, data) {
        if (err) throw err; // Something went wrong!
        var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
        s3bucket.createBucket(function () {
            var params = {
                Key: file.originalFilename, //file.name doesn't exist as a property
                Body: data
            };
            s3bucket.upload(params, function (err, data) {
                // Whether there is an error or not, delete the temp file
                fs.unlink(file.path, function (err) {
                    if (err) {
                        console.error(err);
                    }
                    console.log('Temp File Delete');
                });

                console.log("PRINT FILE:", file);
                if (err) {
                    console.log('ERROR MSG: ', err);
                    res.status(500).send(err);
                } else {
                    console.log('Successfully uploaded data');
                    res.status(200).end();
                }
            });
        });
    });
};

これは何をするかというと、アップロードされたファイルをローカルファイルシステムから読み込んで、S3にアップロードし、一時ファイルを削除してレスポンスを送信するのです。

この方法にはいくつかの問題があります。まず、大きなファイルでは、書き込む前にファイル全体を読み込むことになるので、効率的ではありません。第二に、このプロセスは、大きなファイルのマルチパート アップロードをサポートしていません (マルチパート アップロードを行う前のカットオフ値は 5 Mb だと思います)。

代わりに私が提案したいのは、私が取り組んでいる S3FS これはネイティブの FS に似たインターフェースを提供しますが、マルチパートアップロードや S3 API などの詳細は抽象化されています(再帰的メソッドなどの追加機能もあります)。

もし、あなたが S3FS ライブラリを引っ張ってくるとしたら、コードは次のようになります。

var fs = require('fs'),
    S3FS = require('s3fs'),
    s3fsImpl = new S3FS('mybucketname', {
        accessKeyId: XXXXXXXXXXX,
        secretAccessKey: XXXXXXXXXXXXXXXXX
    });

// Create our bucket if it doesn't exist
s3fsImpl.create();

exports.upload = function (req, res) {
    var file = req.files.file;
    var stream = fs.createReadStream(file.path);
    return s3fsImpl.writeFile(file.originalFilename, stream).then(function () {
        fs.unlink(file.path, function (err) {
            if (err) {
                console.error(err);
            }
        });
        res.status(200).end();
    });
};

これは、提供されたバケットと AWS 認証情報に対してモジュールをインスタンス化し、バケットが存在しない場合は作成するものです。そして、ファイルをアップロードするためのリクエストが来たときに、ファイルへのストリームを開き、それを使って指定したパスに S3 にファイルを書き込みます。これは、(必要であれば)舞台裏でマルチパートのアップロード部分を処理し、ストリームを通じて行われるという利点があるので、アップロードを開始する前にファイル全体を読み込むために待つ必要はありません。

お望みであれば、このコードを プロミス . あるいは pipe() メソッドをイベントリスナーで使用して、終了/エラーを判断することもできます。

もし、いくつかの追加のメソッドを探しているのであれば、s3fsのドキュメントをチェックしてください。