web-dev-qa-db-ja.com

NodeJSを使用してファイルをAmazon S3にアップロードする

S3バケットにファイルをアップロードしようとしたときに問題が発生しました。私のファイルパラメータが適切でないように見えることを除いて、すべてが機能します。 Amazon S3 SDKを使用して、nodejsからs3にアップロードしています。

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

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 paramを"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' }

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

53
Maximus S

そのため、ここでいくつか問題が発生しているようです。投稿によると、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だと思います)。

代わりに私がお勧めするのは、Node.JSのネイティブ FS に似たインターフェースを提供する S3FS という抽象化されたモジュールを使用することですマルチパートアップロードや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に書き込みます。これにより、バックグラウンドでマルチパートアップロードピースが処理され(必要に応じて)、ストリームを介して行われるという利点があるため、アップロードを開始する前にファイル全体を読み取るのを待つ必要はありません。

必要に応じて、コードを Promises からコールバックに変更できます。または、イベントリスナーで pipe() メソッドを使用して、終了/エラーを判断します。

追加のメソッドを探している場合は、s3fsのドキュメントを参照してください。追加のメソッドを探している場合や問題がある場合は、気軽に問題を開いてください。

89
David

または、約束の使用:

const AWS = require('aws-sdk');
AWS.config.update({
    accessKeyId: 'accessKeyId',
    secretAccessKey: 'secretAccessKey',
    region: 'region'
});

let params = {
    Bucket: "yourBucketName",
    Key: 'someUniqueKey',
    Body: 'someFile'
};
try {
    let uploadPromise = await new AWS.S3().putObject(params).promise();
    console.log("Successfully uploaded data to bucket");
} catch (e) {
    console.log("Error uploading data: ", e);
}
4
milosnkb

AWS s3にファイルをアップロードし、ファイルにアクセスするための応答としてURLを送信します。

Multerは、multipart/form-dataを処理するためのnode.jsミドルウェアであり、主にファイルのアップロードに使用されます。効率を最大化するために、busboyの上に書かれています。このnpmモジュールを確認してください here

リクエストを送信するとき、ヘッダーがContent-Typeがmultipart/form-dataであることを確認してください。応答でファイルの場所を送信します。これにより、URLが提供されますが、そのURLにアクセスする場合は、バケットを公開するか、アクセスできなくなります。

upload.router.js

const express = require('express');
const router = express.Router();
const AWS = require('aws-sdk');
const multer = require('multer');
const storage = multer.memoryStorage()
const upload = multer({storage: storage});

const s3Client = new AWS.S3({
    accessKeyId: 'your_access_key_id',
    secretAccessKey: 'your_secret_access_id',
    region :'ur region'
});

const uploadParams = {
         Bucket: 'ur_bucket_name', 
         Key: '', // pass key
         Body: null, // pass file body
};


router.post('/api/file/upload', upload.single("file"),(req,res) => {
    const params = uploadParams;

    uploadParams.Key = req.file.originalname;
    uploadParams.Body = req.file.buffer;

    s3Client.upload(params, (err, data) => {
        if (err) {
            res.status(500).json({error:"Error -> " + err});
        }
        res.json({message: 'File uploaded successfully','filename': 
        req.file.originalname, 'location': data.Location});
    });
});

module.exports = router;

app.js

const express = require('express');
const app = express();

const router = require('./app/routers/upload.router.js');
app.use('/', router);

// Create a Server
  const server = app.listen(8080, () => {
  console.log("App listening at 8080"); 
})
1
Nayan Patel
var express = require('express')

app = module.exports = express();
var secureServer = require('http').createServer(app);
secureServer.listen(3001);

var aws = require('aws-sdk')
var multer = require('multer')
var multerS3 = require('multer-s3')

    aws.config.update({
    secretAccessKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    accessKeyId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    region: 'us-east-1'
    });
    s3 = new aws.S3();

   var upload = multer({
   storage: multerS3({
    s3: s3,
    dirname: "uploads",
    bucket: "Your bucket name",
    key: function (req, file, cb) {
        console.log(file);
        cb(null, "uploads/profile_images/u_" + Date.now() + ".jpg"); //use  
     Date.now() for unique file keys
    }
  })
   });

 app.post('/upload', upload.single('photos'), function(req, res, next) {

 console.log('Successfully uploaded ', req.file)

 res.send('Successfully uploaded ' + req.file.length + ' files!')

})
0
user9442236