teilen einige, Code. Ich konnte nicht den POST-Ansatz arbeiten, aber die PUT. Ben, du musst das bucgkground plugin benutzen (ich habe deinen Ansatz ausprobiert und lege eine leere Datei, die Datei wird nicht erkannt).
aufhören zu reden, hier ist der Code:
var _ = require("underscore");
var moment = require("moment");
var CryptoJS = require("crypto-js");
import fs = require("file-system");
var Buffer = require("buffer/").Buffer;
var bghttp = require("nativescript-background-http");
import { Injectable } from "@angular/core";
import { RequestOptionsArgs, Headers } from "@angular/http";
import { BaseService } from "./base.service";
import { AppConfig } from "../../app.config";
import { Company } from "../dtos";
@Injectable()
export class ImageService extends BaseService {
uploadCompanyLogo(company: number, fileExtension: string, localPath: string) {
let fileName = company + "." + fileExtension;
this.putFileUpload(fileName, localPath, AppConfig.S3_COMPANY_LOGOS_PATH + "/" + fileName, fileExtension);
}
uploadCompanyCoverImage(company: number, fileExtension: string, localPath: string) {
let fileName = company + "." + fileExtension;
this.putFileUpload(fileName, localPath, AppConfig.S3_COMPANY_COVER_IMAGES_PATH + "/" + fileName, fileExtension);
}
private putFileUpload(fileName: string, localPath: string, s3Path: string, fileExtension: string) {
let url = "http://s3.amazonaws.com/" + AppConfig.S3_BUCKET + "/" + s3Path;
let mimeType = "image/" + fileExtension;
//let payloadHash = this.getPayloadHash(payload);
let payloadHash = "UNSIGNED-PAYLOAD";
let date = moment().utc();
let options: RequestOptionsArgs = {
method: "PUT",
headers: new Headers({
"Host": "s3.amazonaws.com", // Mandatory
"Content-Type": mimeType, // Mandatory
//"Content-Length": "10000", // Mandatory: This header is required for PUTs
// When you specify the Authorization header, you must specify either the x-amz-date or the Date header
"x-amz-date": date.format("YYYYMMDD[T]HHmmss[Z]"),
"x-amz-content-sha256": payloadHash, // Mandatory: It provides a hash of the request payload.
//"x-amz-acl": "public-read" // Optional: By default, all objects are private: only the owner has full control.
//"Authorization" // Will be added by addAuthorizationHeader
//"Content-MD5" // Recommended: The base64 encoded 128-bit MD5 digest of the message
})
};
// Adding the authorization header
let authorization = this.getAuthorizationHeader(options,
AppConfig.S3_ACCESS_KEY_ID, AppConfig.S3_ACCESS_KEY_SECRET,
AppConfig.S3_REGION, AppConfig.S3_BUCKET, s3Path, date, payloadHash);
options.headers.append("Authorization", authorization);
let session = bghttp.session("image-services");
let request = {
url: url,
method: "PUT",
headers: {
"Host": "s3.amazonaws.com", // Mandatory
"Content-Type": mimeType, // Mandatory
//"Content-Length": "10000", // Mandatory: This header is required for PUTs
// When you specify the Authorization header, you must specify either the x-amz-date or the Date header
"x-amz-date": date.format("YYYYMMDD[T]HHmmss[Z]"),
"x-amz-content-sha256": payloadHash, // Mandatory: It provides a hash of the request payload.
//"x-amz-acl": "public-read" // Optional: By default, all objects are private: only the owner has full control.
//"Authorization" // Will be added by addAuthorizationHeader
//"Content-MD5" // Recommended: The base64 encoded 128-bit MD5 digest of the message
"Authorization": authorization
},
description: "{ 'Uploading': '" + fileName + "' }"
};
var task = session.uploadFile("file://" + localPath, request);
//task.on("progress", (e) => console.log(e));
//task.on("error", (e) => console.log(e));
//task.on("complete", (e) => console.log(e));
}
/**
* http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTForms.html
*/
private postFileUpload(fileName: string, localPath: string, s3Path: string, fileExtension: string) {
let date = moment().utc();
let xhr = new XMLHttpRequest();
// action – The URL that processes the request, which must be set to the URL of the bucket.
// For example, if the name of your bucket is examplebucket, the URL is http://examplebucket.s3.amazonaws.com/.
let url = "http://" + AppConfig.S3_BUCKET + ".s3.amazonaws.com/";
xhr.open("POST", url);
xhr.setRequestHeader("Content-Type", "multipart/form-data");
// The policy required for making authenticated requests using HTTP POST is a UTF-8 and Base64 encoded document
// written in JavaScript Object Notation (JSON) that specifies conditions that the request must meet.
let expiration = moment().add(7, "days").utc();
let credential = this.getCredential(AppConfig.S3_ACCESS_KEY_ID, AppConfig.S3_REGION, date);
let policy = {
// The POST policy always contains the expiration and conditions elements.
"expiration": expiration.format("YYYY-MM-DD[T]HH:mm:ss[Z]"), // e.g: "2007-12-01T12:00:00.000Z"
// Each form field that you specify in a form (except x-amz-signature, file, policy, and field names that have an x-ignore- prefix)
// must appear in the list of conditions.
"conditions": [
{ "bucket": AppConfig.S3_BUCKET },
[ "starts-with", "$Content-Type", "image/" ],
[ "starts-with", "$key", "" ],
//policy is not required
{ "x-amz-algorithm": "AWS4-HMAC-SHA256" },
{ "x-amz-credential": credential },
[ "starts-with", "$x-amz-date", "" ]
//x-amz-signature is not required
//file is not required
]
};
let encodedPolicy = this.getEncodedPolicy(policy);
let signature = this.getSignature(encodedPolicy, date,
AppConfig.S3_ACCESS_KEY_SECRET, AppConfig.S3_REGION);
var formData = new FormData();
formData.append("Content-Type", "image/" + fileExtension);
formData.append("key", s3Path);
formData.append("policy", encodedPolicy);
formData.append("x-amz-algorithm", "AWS4-HMAC-SHA256");
formData.append("x-amz-credential", credential);
formData.append("x-amz-date", date.format("YYYYMMDD[T]HHmmss[Z]"));
formData.append("x-amz-signature", signature);
//console.log("Date: " + date.format("YYYYMMDD[T]HHmmss[Z]"));
//console.log("Credential: " + credential);
//console.log("Policy: " + encodedPolicy);
//console.log("Signature: " + signature);
let file = fs.File.fromPath(localPath);
//let payload = file.readSync(error => console.log(error));
// This fails because file is not recognized as file so added as toString()
formData.append("file", file, fileName); // file or payload
xhr.onload =() => {
console.log("Response Text" + xhr.responseText);
console.log("XHR: ", JSON.stringify(xhr));
if (xhr.status === 200) {
console.log('Uploaded!');
}
};
xhr.onprogress =() => {
console.log("Progress!");
}
xhr.onerror = (error) => {
console.log("Could not upload file: " + error);
};
xhr.send(formData);
}
protected getEncodedPolicy(policy): string {
return new Buffer((typeof policy == "string") ? policy : JSON.stringify(policy)).toString("base64");
}
/**
* From: http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTCommonRequestHeaders.html
* Just the date for this service.
*/
protected getAuthorizationHeader(options: RequestOptionsArgs, s3Key: string, s3Secret: string,
s3Region: string, s3Bucket: string, s3Path: string, date, payloadHash: string): string {
// Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,
let credential = this.getCredential(s3Key, s3Region, date);
// SignedHeaders=host;range;x-amz-date,
// A semicolon-separated list of request headers that you used to compute Signature.
// The list includes header names only, and the header names must be in lowercase.
let signedHeaders = _(options.headers.keys())
.chain()
.map(function(hdr) { return hdr.toLowerCase(); })
.sortBy(function(hdr) { return hdr; }) // It is not required
.value()
.join(";");
// Signature=fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024
// The 256-bit signature expressed as 64 lowercase hexadecimal characters.
let strToSign = this.getStrToSign(options, date, s3Region, s3Bucket, s3Path, payloadHash);
let signature = this.getSignature(strToSign, date, s3Secret, s3Region).toLowerCase();
// Authorization: AWS4-HMAC-SHA256 Credential=...,SignedHeaders=...,Signature=...
// There is space between the first two components, AWS4-HMAC-SHA256 and Credential
// The subsequent components, Credential, SignedHeaders, and Signature are separated by a comma.
let authorization = "AWS4-HMAC-SHA256 Credential=" + credential +
",SignedHeaders=" + signedHeaders + ",Signature=" + signature;
return authorization;
}
protected getCredential(s3Key: string, s3Region: string, date) {
// Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,
// Your access key ID and the scope information, which includes the date, region, and service that were used to calculate the signature.
return s3Key + "/" + date.format("YYYYMMDD") + "/" + s3Region + "/s3/aws4_request";
}
/**
* From: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
* @return a strToSign for this request.
*/
protected getStrToSign(options: RequestOptionsArgs, date, s3Region: string, s3Bucket: string, s3Path: string, payloadHash: string) {
// "AWS4-HMAC-SHA256" + "\n" +
// timeStampISO8601Format + "\n" +
// <Scope> + "\n" +
// Hex(SHA256Hash(<CanonicalRequest>))
let cannonicalRequest = this.getCannonicalRequest(options, s3Bucket, s3Path, payloadHash);
let strToSign = "AWS4-HMAC-SHA256\n";
//timeStampISO8601Format
strToSign += date.format("YYYYMMDD[T]HHmmss[Z]") + "\n";
// Scope binds the resulting signature to a specific date, an AWS region, and a service.
// Thus, your resulting signature will work only in the specific region and for a specific service.
// The signature is valid for seven days after the specified date.
strToSign += date.format("YYYYMMDD") + "/" + s3Region + "/s3/aws4_request" + "\n";
//Hex(SHA256Hash(cannonicalRequest))
//SHA256Hash(): Secure Hash Algorithm (SHA) cryptographic hash function.
strToSign += CryptoJS
.SHA256(cannonicalRequest)
.toString(CryptoJS.enc.Hex);
//console.log("<StrToSign>" + strToSign + "<StrToSignEnds>");
return strToSign;
}
protected getPayloadHash(payload):string {
return CryptoJS
.SHA256(payload)
.toString(CryptoJS.enc.Hex); // Not really necessary
}
/**
* From: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
* @return the cannonical request for this request.
*/
protected getCannonicalRequest(options: RequestOptionsArgs, s3Bucket: string, s3Path: string, payloadHash: string) {
// <HTTPMethod>\n
// <CanonicalURI>\n
// <CanonicalQueryString>\n
// <CanonicalHeaders>\n
// <SignedHeaders>\n
// <HashedPayload>
let cannonicalRequest = "";
// <HTTPMethod>\n
cannonicalRequest += options.method + "\n";
// <CanonicalURI>\n
// CanonicalURI is the URI-encoded version of the absolute path component of the URI —
// everything starting with the "/" that follows the domain name and up to the end of the string or
// to the question mark character ('?') if you have query string parameters.
if (options.headers.get("Host").lastIndexOf(s3Bucket) != -1) {
cannonicalRequest += "/" + encodeURI(s3Path) + "\n";
} else {
cannonicalRequest += "/" + encodeURI(s3Bucket) + "/" + encodeURI(s3Path) + "\n";
}
// <CanonicalQueryString>\n
cannonicalRequest += "\n"; // There is no query string
// <CanonicalHeaders>\n
// CanonicalHeaders is a list of request headers with their values.
// Individual header name and value pairs are separated by the newline character ("\n").
// Header names must be in lowercase. You must sort the header names alphabetically to construct the string
// The CanonicalHeaders list must include the following:
// - HTTP host header.
// - If the Content-Type header is present in the request, you must add it to the CanonicalHeaders list.
// - The x-amz-content-sha256 header is required for all AWS Signature Version 4 requests. It provides a hash of the request payload.
let headers = _(options.headers.keys())
.chain()
.map(function(v, k) { return v.toLowerCase() + ":" + options.headers.get(v).trim(); })
.sortBy(function(v, k) { return v.split(":")[0]; })
.value()
.join("\n");
cannonicalRequest += headers + "\n";
cannonicalRequest += "\n"; // ?
// <SignedHeaders>\
// SignedHeaders is an alphabetically sorted, semicolon-separated list of lowercase request header names.
let signedHeaders = _(options.headers.keys())
.chain()
.map(function(hdr) { return hdr.toLowerCase(); })
.sortBy(function(hdr) { return hdr; })
.value()
.join(";");
cannonicalRequest += signedHeaders + "\n";
// <HashedPayload>
cannonicalRequest += payloadHash;
//console.log("<CannonicalRequest>" + cannonicalRequest + "<CannonicalRequestEnds>");
return cannonicalRequest;
}
/**
* From: http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html
* @return a signature for this request.
*/
protected getSignature(strToSign: string, date, s3Secret, s3Region) {
// HMAC-SHA256(): Computes HMAC by using the SHA256 algorithm with the signing key provided. This is the final signature.
// https://code.google.com/archive/p/crypto-js/
// var hash = CryptoJS.HmacSHA256("Message", "Secret Passphrase");
// WARNING: The way amazon presents the key/phrase is the oposite to the method signature
// DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>")
let dateKey = CryptoJS.HmacSHA256(date.format("YYYYMMDD"), "AWS4" + s3Secret);
// DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>")
let dateRegionKey = CryptoJS.HmacSHA256(s3Region, dateKey);
// DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>")
let dateRegionServiceKey = CryptoJS.HmacSHA256("s3", dateRegionKey);
// SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request")
let signKey = CryptoJS.HmacSHA256("aws4_request", dateRegionServiceKey);
// sign the request string
let signature = CryptoJS
.HmacSHA256(strToSign, signKey)
.toString(CryptoJS.enc.Hex);
//console.log("Signature :", signature);
return signature;
}
}
Und hier ist ein Test für den Algorithmus (von Amazon extrahiert). Es gibt ein Problem mit $ in dem Dateinamen (der zweite Fall), aber ich habe darüber nicht zu kümmern (es ist nicht encodeURIComponent):
var moment = require("moment");
import "reflect-metadata";
import { ImageService } from "../../shared/services/image.service";
import { RequestOptionsArgs, Headers } from "@angular/http";
import { RequestOptions } from "@angular/http";
declare var describe;
declare var it;
declare var expect;
class ImageServiceTest extends ImageService {
public getEncodedPolicy(policy): string {
return super.getEncodedPolicy(policy);
}
public getAuthorizationHeader(options, s3Key: string, s3Secret: string, s3Region: string, s3Bucket: string, s3Path: string, date, payload): string {
return super.getAuthorizationHeader(options, s3Key, s3Secret, s3Region, s3Bucket, s3Path, date, payload);
}
public getCannonicalRequest(options, s3Bucket: string, s3Path: string, payload) {
return super.getCannonicalRequest(options, s3Bucket, s3Path, payload);
}
public getPayloadHash(payload):string {
return super.getPayloadHash(payload);
}
public getStrToSign(options, date, s3Region: string, s3Bucket: string, s3Path: string, payload) {
return super.getStrToSign(options, date, s3Region, s3Bucket, s3Path, payload);
}
public getSignature(strToSign: string, date, s3Secret, s3Region) {
return super.getSignature(strToSign, date, s3Secret, s3Region);
}
}
describe("GET S3 AWS Test:", function() {
let imageService = new ImageServiceTest();
let date = moment("20130524", "YYYYMMDD");
// Taken from: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
let s3Region = "us-east-1";
let s3Bucket = "examplebucket";
let s3Path = "test.txt";
let payload = "";
let s3Key = "AKIAIOSFODNN7EXAMPLE";
let s3Secret = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
let payloadHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let options: RequestOptionsArgs = {
method: "GET",
headers: new Headers({
"Host": "examplebucket.s3.amazonaws.com",
"Range": "bytes=0-9",
"x-amz-content-sha256": payloadHash,
"x-amz-date": date.format("YYYYMMDD[T]HHmmss[Z]")
})
};
let expectedCannonicalRequest =
"GET\n" +
"/test.txt\n" +
"\n" + // No query parameters
"host:examplebucket.s3.amazonaws.com\n" +
"range:bytes=0-9\n" +
"x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n" +
"x-amz-date:20130524T000000Z\n" +
"\n" + // ??
"host;range;x-amz-content-sha256;x-amz-date\n" +
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
let expectedStringToSign =
"AWS4-HMAC-SHA256\n" +
"20130524T000000Z\n" +
"20130524/us-east-1/s3/aws4_request\n" +
"7344ae5b7ee6c3e7e6b0fe0640412a37625d1fbfff95c48bbb2dc43964946972";
let expectedSignature = "f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41";
let expectedAuthorizationHeader = "AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;range;x-amz-content-sha256;x-amz-date,Signature=f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41";
it ("Check the GET PayloadHash.", function() {
expect(imageService.getPayloadHash(payload)).toEqual(payloadHash);
});
it ("Check the GET CanonicalRequest.", function() {
expect(imageService.getCannonicalRequest(options, s3Bucket, s3Path, payloadHash)).toEqual(expectedCannonicalRequest);
});
it ("Check the GET StringToSign.", function() {
expect(imageService.getStrToSign(options, date, s3Region, s3Bucket, s3Path, payloadHash)).toEqual(expectedStringToSign);
});
it ("Check the GET Signature.", function() {
expect(imageService.getSignature(expectedStringToSign, date, s3Secret, s3Region)).toEqual(expectedSignature);
});
it ("Check the GET Authorization Header.", function() {
expect(imageService.getAuthorizationHeader(options, s3Key, s3Secret, s3Region, s3Bucket, s3Path, date, payloadHash)).toEqual(expectedAuthorizationHeader);
});
});
Dank! Ich arbeite gerade an einem anderen Teil dieses Projekts, aber ich werde dieses Wochenende versuchen, Sie wissen zu lassen. –