18

Próbuję uzyskać wersję umożliwiającą użytkownikom przesyłanie plików bezpośrednio do mojego zasobnika Amazon S3 z witryny sieciowej NodeJS. Wygląda na to, że jedyne tutoriale poza nim są inne niż wszystkie.Aplikacja Amazon S3 POST api i podpisywanie polisy z NodeJS

Podążam za this tutorial, aby uzyskać podstawowe informacje, ale znowu jest przestarzały. Nie ma poprawnych wywołań metody do crypto, ponieważ próbuje przekazać surowy obiekt JavaScript do metody update, która zgłasza błąd, ponieważ nie jest łańcuchem ani buforem.

Ja również szukałem źródła dla the knox npm package. Nie ma wbudowanego wsparcia POST - co rozumiem całkowicie, ponieważ to przeglądarka robi POST, gdy ma właściwe pola. Wygląda na to, że Knox ma odpowiedni kod do podpisania polityki, a ja próbowałem sprawić, by mój kod działał w oparciu o to ... ale znowu bez skutku.

Oto, co wymyśliłem, dla kodu. Tworzy zasadę zakodowaną w base64 i tworzy podpis ... ale jest to zła sygnatura zgodna z Amazon, kiedy próbuję przesłać plik.


var crypto = require("crypto"); 
var config = require("../../amazonConfig.json"); 

exports.createS3Policy = function(callback) { 
    var date = new Date(); 

    var s3Policy = { 
    "expiration": "2014-12-01T12:00:00.000Z", 
    "conditions": [ 
     {"acl": "public-read"}, 
     ["content-length-range", 0, 2147483648], 
     {"bucket": "signalleaf"}, 
     ["starts-with", "$Cache-Control", ""], 
     ["starts-with", "$Content-Type", ""], 
     ["starts-with", "$Content-Disposition", ""], 
     ["starts-with", "$Content-Encoding", ""], 
     ["starts-with", "$Expires", ""], 
     ["starts-with", "$key", "/myfolder/"], 
     {"success_action_redirect": "http://example.com/uploadsuccess"}, 
    ] 
    }; 

    var stringPolicy = JSON.stringify(s3Policy).toString("utf-8"); 
    var buffer = Buffer(stringPolicy, "utf-8"); 

    var encoded = buffer.toString("base64"); 
    var signature = crypto.createHmac("sha1", config.secretKey) 
    .update(new Buffer(stringPolicy, "utf-8")).digest("base64"); 


    var s3Credentials = { 
    s3PolicyBase64: encoded, 
    s3Signature: signature 
    }; 

    GLOBAL.s3creds = s3Credentials; 

    callback(s3Credentials); 
}; 

Oczywiście robię coś złego tutaj. Ale nie mam pojęcia co. Czy ktoś może pomóc w identyfikacji tego, co robię źle? Gdzie jest mój problem? Czy ktoś ma działający samouczek, w jaki sposób wygenerować odpowiednią politykę Amazon S3, z podpisem, z NodeJS v0.10.x, dla POST do s3 REST API?

+1

Przesyłanie plików bezpośrednio do S3 nie jest to banalne zadanie, zwłaszcza jeśli chcesz wesprzeć wyrwy, automatyczne wznawianie metadanych użytkownika, itp, itd. Rzeczy polityka _możesz_ być dość skomplikowane. Rozważ użycie biblioteki, którą utrzymuję: [Fine Uploader] (http://fineuploader.com). Ma natywną obsługę bezpośredniego przesyłania do S3 we wszystkich przeglądarkach, nawet IE7. Chunking i automatyczne wznawianie, oprócz innych funkcji, są również obsługiwane. Ponadto napisałem [przykład serwera po stronie serwera] (http://bit.ly/1do27a0), który po sparowaniu z programem Fine Uploader S3 obsłuży wszystkie twoje podpisy. –

+0

Czy możesz opublikować ten komentarz jako odpowiedź? może skończyć się używanie twojej biblioteki. wciąż oceniam, jak to działa, itp. –

+1

Nie jestem pewien, czy to się dobrze skończy. Można to uznać za odpowiedź ubogą lub łączącą, szczerze mówiąc. Rozumiem, że społeczność szuka szczegółowych odpowiedzi, które zawierają kod, a mój nie pasuje do tego opisu, dlatego opublikowałem go jako komentarz. Jeśli masz jakieś pytania dotyczące Fine Uploader, zajrzyj do tagu fine-uploader na SO, gdzie zajmujemy się obsługą pytań do biblioteki. –

Odpowiedz

34

OK, w końcu to rozgryzłem. Po odtwarzanie losowe gry zgadywania przez bardzo długi czas, pomyślałem sobie

"może muszę podpisać base64 zakodowany politykę" - me

i BAM, że to było.

Ja również ponownie zamówiłem warunki, które pasują do tego, jak formularz jest wysyłany, ale nie jestem pewien, czy to ma znaczenie.

var crypto = require("crypto"); 
var config = require("../../amazonConfig.json"); 

exports.createS3Policy = function(contentType, callback) { 
    var date = new Date(); 

    var s3Policy = { 
    "expiration": "2014-12-01T12:00:00.000Z", // hard coded for testing 
    "conditions": [ 
     ["starts-with", "$key", "somefolder/"], 
     {"bucket": "my-bucket-name"}, 
     {"acl": "public-read"}, 
     ["starts-with", "$Content-Type", contentType], 
     {"success_action_redirect": "http://example.com/uploadsuccess"}, 
    ] 
    }; 

    // stringify and encode the policy 
    var stringPolicy = JSON.stringify(s3Policy); 
    var base64Policy = Buffer(stringPolicy, "utf-8").toString("base64"); 

    // sign the base64 encoded policy 
    var signature = crypto.createHmac("sha1", config.secretKey) 
    .update(new Buffer(base64Policy, "utf-8")).digest("base64"); 

    // build the results object 
    var s3Credentials = { 
    s3Policy: base64Policy, 
    s3Signature: signature 
    }; 

    // send it back 
    callback(s3Credentials); 
}; 

Mam nadzieję, że pomoże to innym osobom, które mają ten sam problem.

+0

DZIĘKUJEMY! Ten kod pomógł mi. Kilka szybkich komentarzy: Aby sformatować datę użyłem [moment.js] (http://momentjs.com/docs/) w następujący sposób: 'moment.utc (expirationDate) .format ('YYYY-MM-DD') + ' T '+ moment.utc (expirationDate) .format (' HH: mm: ss.SSS ') +' Z''. Również dla buforów 'utf8' (uwaga: brak łącznika) jest domyślnym kodowaniem, więc myślę, że "utf-8" jest niepoprawny i nie jest obcy. – Zugwalt

+4

@Zugwalt, możesz to uprościć nieco dzięki wbudowanemu formatowaniu. 'moment.utc (expirationDate) .toISOString()' – Jonathan

+0

@ Jonathan jeszcze lepszy! Dzięki! – Zugwalt

14

Zmodyfikowałem nieco poprzedni przykład, ponieważ nie zadziałało to dla mnie: amazon zwrócił błąd dotyczący uszkodzonego podpisu.

Oto jak podpis powinien zostać utworzony podczas przesyłania oparty na przeglądarce Korzystanie POST (AWS Signature Version 4)

http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-authentication-HTTPPOST.html

Calculating a Signature

var CryptoJS = require("crypto-js"); 

var accessKeyID = "PUT YOUR DATA"; 
var secretAccessKey = "PUT YOUR DATA"; 

var bucket = "PUT YOUR BUCKET NAME"; 
var region = "eu-central-1"; // overwrite with your region 
var folder = "users/"; // overwrite with your folder 
var expiration = "2015-09-28T12:00:00.000Z"; // overwrite date 
var date = "20150927"; // overwrite date 
var serviceName = "s3"; 


function getSignatureKey(key, dateStamp, regionName, serviceName) { 
    var kDate = CryptoJS.HmacSHA256(dateStamp, "AWS4" + key); 
    var kRegion = CryptoJS.HmacSHA256(regionName, kDate); 
    var kService = CryptoJS.HmacSHA256(serviceName, kRegion); 
    var kSigning = CryptoJS.HmacSHA256("aws4_request", kService); 

    return kSigning; 
} 

var s3Policy = {"expiration": expiration, 
    "conditions": [ 
    {"bucket": bucket}, 
    ["starts-with", "$key", folder], 
    {"acl": "public-read"}, 
    ["starts-with", "$Content-Type", "image/"], 
    {"x-amz-meta-uuid": "14365123651274"}, 
    ["starts-with", "$x-amz-meta-tag", ""], 
    {"x-amz-credential": accessKeyID + "/" + date + "/" + region + "/" + serviceName +"/aws4_request"}, 
    {"x-amz-algorithm": "AWS4-HMAC-SHA256"}, 
    {"x-amz-date": date + "T000000Z" } 
    ] 
}; 

var base64Policy = new Buffer(JSON.stringify(s3Policy), "utf-8").toString("base64"); 
console.log('base64Policy:', base64Policy); 

var signatureKey = getSignatureKey(secretAccessKey, date, region, serviceName); 
var s3Signature = CryptoJS.HmacSHA256(base64Policy, signatureKey).toString(CryptoJS.enc.Hex); 
console.log('s3Signature:', s3Signature); 

Następny generowane base64Policy i s3Signature użyłem w formularz do przesłania. Przykład jest tutaj: http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html

Bardzo ważne jest sprawdzenie, czy w formularzu HTML iw polisie znajdują się te same pola i wartości.

1

I nadal mają problemy, więc pracował przez nich i wysłane moje rozwiązanie tutaj:

https://github.com/nikkwong/ng2-s3-uploader

W skrócie, jeśli się z odpowiedzią scabbiaza w budowaniu podpis, upewnij się, aby zbudować formę tak:

let formData = new FormData; 
formData.append('acl', xAmzAcl); 
formData.append('Content-Type', file.type); 
formData.append('X-Amz-Date', xAmzDate); 
formData.append('x-amz-server-side-encryption', xAmzServerSideEncryption); 
formData.append('x-amz-meta-uuid', xAmzMetaUuid); 
formData.append('X-Amz-Algorithm', xAmzAlgorithm); 
formData.append('X-Amz-Credential', xAmzCredential); 
formData.append('X-Amz-Signature', s3Signature); 
formData.append('Policy', base64Policy); 
formData.append('key', folder + '/' + file.name); 
// File field must come last! 
formData.append('file', file); 
+0

Dane twojego formularza nie zawierają "x-amz-meta-tag". –

+1

To nadal będzie działało bez względu na to, właśnie sprawdziłem. –