Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/amazon-s3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Amazon s3 nativescript后台http文件上载到s3_Amazon S3_Nativescript - Fatal编程技术网

Amazon s3 nativescript后台http文件上载到s3

Amazon s3 nativescript后台http文件上载到s3,amazon-s3,nativescript,Amazon S3,Nativescript,基于 我正在尝试编写一个类来处理文件上传到s3的问题 我的上传功能如下所示: upload() { const format = enumsModule.ImageFormat.png cameraModule.takePicture().then(img => { let savePath = fsModule.knownFolders.documents().path; console.log('Save Path', savePath)

基于

我正在尝试编写一个类来处理文件上传到s3的问题

我的上传功能如下所示:

upload() {
    const format = enumsModule.ImageFormat.png
    cameraModule.takePicture().then(img => {
        let savePath = fsModule.knownFolders.documents().path;
        console.log('Save Path', savePath);
        let fileName = "img_" + new Date().getTime() + "." + format;
        console.log('fileName', fileName);
        let filePath = fsModule.path.join(savePath, fileName);
        console.log('FilePath', filePath);
        if (img.saveToFile(filePath, format)) {
            let s3Upload = new S3Upload(format, fileName, filePath, this.progressCallback);
            s3Upload.getSignedRequest()
                .then((url) => {
                    alert(url);
                })
                .catch(error => {
                    console.log('Error!');
                    alert(error);
                })
        }
    });
}
progressCallback(e: any) {
    console.log(e);
}
我的文件上传程序如下所示:

import {Config} from "../../../shared/config";
import {SignS3Response} from "./signs3Response";

export class S3Upload {
    fileType: String;
    fileName: String;
    filePath: String;
    progressCallback: (e) => void;
    constructor(fileType: String, fileName: String, filePath: String, progressCallback: (e) => void) {
        this.fileType = fileType;
        this.fileName = fileName;
        this.filePath = filePath;
        this.progressCallback = progressCallback;
    }
    getSignedRequest() {
        var xhr = new XMLHttpRequest();
        return new Promise<String>((resolve, reject) => {
            let urlString = Config.apiUrl + "profile/signS3?fileName=" + this.fileName + "&fileType=" + this.fileType;
            console.log(urlString);

            xhr.open("GET", urlString);
            xhr.onload = () => {
                resolve(xhr.response);
            }
            xhr.onerror = () => {
                reject(xhr.response)
            }
            xhr.send();
        })
            .then(() => {
                console.log(JSON.parse(xhr.responseText));
                let response: SignS3Response = <SignS3Response>JSON.parse(xhr.responseText);
                return this.uploadFile(response.signedRequest, response.url);
            })
    }
    uploadFile(signedRequest, url) {
        return new Promise<String>((resolve, reject) => {
            return new Promise<String>((resolve, reject) => {
            var xhr = new XMLHttpRequest();
            xhr.open("PUT", signedRequest);
            xhr.setRequestHeader('x-amz-acl', 'public-read');
            xhr.onload = () => {
                console.log("onload, outside", JSON.stringify(xhr));
                if (xhr.status === 200) {
                    console.log('Uploaded!');
                }
            };
            xhr.onprogress = () => {
                console.log("Progress!");
            }
            xhr.onerror = () => {
                alert("Could not upload file.");
            };
            xhr.send(fs.File.fromPath(this.filePath));
                });
            }
      });
}
我知道这是可行的,因为我是在一个typescript transcompiled博客上做的,唯一的区别是,我从表单的一部分获取文件,这里我使用fs.file.fromPath()

我甚至试图让它进入我的服务器,在我的S3日志中,我有这样一行:a01fa7fe68cb4649bd0d6bc76055584010ef30abc23d1b8968ae1494dfde1dc8 benaychhio[02/Jun/2016:01:16:42+0000]128.177.172.220-2473BE6AA6033D7B REST.PUT.OBJECT 146482838666.png“PUT/146482438666.png?AWSAccessKeyId=AKIAJFUTN7F7VLAR2SCQ&Content Type=png&Expires=146482498&Signature={Signature is here}&x-amz-acl=public read HTTP/1.1”403 AccessDenied 333-4-“-”“montMatchMobile/1.0 CFNetwork/758.3.15 Darwin/15.5.0”-

有人对此有什么建议吗?我也尝试了nativescript后台html,但没有得到任何回报。

尝试以下方法

tns plugin add nativescript-background-http
我改变了一些事情,插件添加了一个很好的本地上传状态通知,正如名字所说,当你的应用程序在后台/最小化时,它可以上传文件

var bghttp = require("nativescript-background-http");

var session = bghttp.session("file-upload");


uploadFile(signedRequest, url) {

    return new Promise<String>((resolve, reject) => {

        var request = {
            url: signedRequest,
            method: "PUT",
            headers: {
                "Content-Type": "application/octet-stream",
                "File-Name": this.fileName,
                "x-amz-acl": 'public-read'
            },
            description: "{ 'uploading': this.fileName }"
        };

        var task = session.uploadFile(this.filePath, request);

        task.on("progress", logEvent);
        task.on("error", logEvent);
        task.on("complete", logEvent);

        function logEvent(e) {
            console.log(e.eventName);
        }

    });

});
var bghttp=require(“nativescript后台http”);
var session=bghttp.session(“文件上传”);
上载文件(signedRequest,url){
返回新承诺((解决、拒绝)=>{
var请求={
url:signedRequest,
方法:“放”,
标题:{
“内容类型”:“应用程序/八位字节流”,
“文件名”:this.fileName,
“x-amz-acl”:“公共读取”
},
说明:“{‘上载’:this.fileName}”
};
var task=session.uploadFile(this.filePath,request);
任务。关于(“进度”,logEvent);
task.on(“错误”,logEvent);
任务。打开(“完成”,logEvent);
函数logEvent(e){
console.log(例如eventName);
}
});
});
共享一些,代码。 我无法让POST方法工作,但是PUT。 Ben,你必须使用bucgkground插件(我尝试了你的方法,并放置了一个空文件,该文件无法识别)

别说了,下面是代码:

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

}

谢谢!我现在正在处理这个项目的另一部分,但我将在这个周末尝试,我会让你知道。你有这个工作代码作为在线的示例吗?我恐怕没有@Zahid我移动到后端做同样的事情。
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;
    }

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