2015-01-19 21 views
37

Wystąpił problem podczas próby przesłania pliku do mojego zasobnika S3. Wszystko działa z wyjątkiem tego, że moje parametry plików nie wydają się odpowiednie. Korzystam z Amazon S3 sdk, aby przesłać z nodejs do s3.Załaduj plik do Amazon S3 z NodeJS

Są to moje ustawienia trasy:

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

Jest items.upload function():

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

Ustawianie Body param na sznurku jak "hello" działa dobrze. Zgodnie z doc, parametrmusi przyjmować dane obiektu (Bufor, Tablica Typowa, Blob, Ciąg, OdczytywanyStream). Jednak przesyłając obiekt pliku nie powiedzie się z następującym komunikatem o błędzie:

[Error: Unsupported body payload object] 

Jest to obiekt pliku:

{ 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' } 

Każda pomoc będzie bardzo mile widziane!

+0

Serwer tylko trzeba przechowywać AWS sekrety tworzenia S3-zarejestrowany adres URL (za pomocą aws-sdk). Klient pobiera podpisany adres URL, każde przesłanie, a następnie stanie się stroną klienta przy użyciu tego adresu URL. Sprawdź ten przewodnik: https://devcenter.heroku.com/articles/s3-upload-node – rocketspacer

Odpowiedz

63

Wygląda więc na to, że dzieje się tu kilka rzeczy. Na podstawie Twojego wpisu wygląda na to, że próbujesz obsługiwać przesyłanie plików przy użyciu oprogramowania pośredniego connect-multiparty. To, co robi to oprogramowanie pośrednie, to pobranie przesłanego pliku, zapisanie go w lokalnym systemie plików, a następnie ustawienie req.files do przesłanego pliku (ów).

Konfiguracja trasy wygląda dobrze, problem wygląda na skutek Twojej funkcji items.upload(). W szczególności w tej części:

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

Jak wspomniałem na początku mojej odpowiedzi connect-multiparty zapisuje plik do lokalnego systemu plików, więc trzeba otworzyć plik i go odczytać, a następnie przesłać go, a następnie usuń go w lokalnym systemie plików.

Mimo to można zaktualizować swoją metodę do czegoś, co następuje:

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(); 
       } 
      }); 
     }); 
    }); 
}; 

Co to jest odczytywany przesłanego pliku z lokalnego systemu plików, a następnie przesłane ją do S3, a następnie usuwa plik tymczasowy i wysyła odpowiedź.

Jest kilka problemów z tym podejściem. Po pierwsze, nie jest to tak efektywne, jak mogłoby być, ponieważ w przypadku dużych plików ładujesz cały plik przed jego napisaniem. Po drugie, ten proces nie obsługuje przesyłania wielu części dla dużych plików (myślę, że odcięcie wynosi 5 Mb, zanim będzie trzeba przesłać wiele części).

Zamiast tego sugeruję używanie modułu, nad którym pracowałem, o nazwie S3FS, który zapewnia podobny interfejs do natywnej wersji FS w pliku Node.JS, ale usuwa niektóre szczegóły, takie jak przesłanie wielu części. i S3 api (jak również dodaje kilka dodatkowych funkcji, takich jak metody rekursywne).

Jeśli było ciągnąć w bibliotece S3FS Twój kod będzie wyglądać następująco:

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(); 
    }); 
}; 

Co to będzie zrobić, to instancję modułu do dostarczonego wiadra i poświadczeń AWS, a następnie utworzyć wiadro jeśli to nie istnieje. Następnie, gdy pojawi się żądanie przesłania pliku, otworzymy strumień do pliku i wykorzystamy go do zapisania pliku w S3 do podanej ścieżki. Spowoduje to obsłużenie wieloczęściowego elementu ładującego za kulisami (jeśli jest to konieczne) i ma tę zaletę, że zostanie wykonane za pomocą strumienia, więc nie musisz czekać na przeczytanie całego pliku, zanim zaczniesz go przesyłać.

Jeśli wolisz, możesz zmienić kod na oddzwonienia z Promises. Lub użyj metody pipe() z detektorem zdarzeń, aby określić koniec/błędy.

Jeśli szukasz dodatkowych metod, zapoznaj się z dokumentacją s3fs i nie wahaj się otworzyć problemu, jeśli szukasz dodatkowych metod lub problemów.

+11

Przykro mi to słyszeć, że jestem niegrzeczny. Niestety, to nie ja odrzuciłem edycję, bo to była społeczność. Więc sam dokonałem edycji w ten sposób, że odzwierciedla ona poprawny kod. Pozdrawiam człowieka! – David

+2

Mam wiadro i folder w nim. Jak mogę określić listę ACL dla obiektu, który zostanie przesłany do tego folderu. Zmieniono uprawnienia folderów na "Udostępnij publicznie", ale za każdym razem, gdy nowy obiekt jest przesyłany, nie jest on publiczny. Próbowałem acl: "public-read" w konstruktorze S3FS, ale to nie działa. – Dan

+0

Jeśli używasz S3FS podczas pisania obiektu, możesz określić listę ACL. Sprawdź ten link: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property W przeciwnym razie musisz określić zasadę podczas przesyłania pliku poza S3FS. – David

5

znalazłem następujące być roztwór roboczy ::

npm install aws-sdk 


Po zainstalowaniu AWS-SDK, należy użyć następującego kodu zastępując wartości ze swoją razie potrzeby.

var AWS = require('aws-sdk'); 
var fs = require('fs'); 

var s3 = new AWS.S3(); 

// Bucket names must be unique across all S3 users 

var myBucket = 'njera'; 

var myKey = 'jpeg'; 
//for text file 
//fs.readFile('demo.txt', function (err, data) { 
//for Video file 
//fs.readFile('demo.avi', function (err, data) { 
//for image file     
fs.readFile('demo.jpg', function (err, data) { 
    if (err) { throw err; } 



    params = {Bucket: myBucket, Key: myKey, Body: data }; 

    s3.putObject(params, function(err, data) { 

     if (err) { 

      console.log(err) 

     } else { 

      console.log("Successfully uploaded data to myBucket/myKey"); 

     } 

     }); 

}); 

znalazłem kompletny poradnik na ten temat tutaj w przypadku szukasz referencje ::


How to upload files (text/image/video) in amazon s3 using node.js