基于angular / quickstart上传到Angular 2项目中的AWS S3存储桶

时间:2017-02-01 00:59:41

标签: angular amazon-s3 cors

我需要从Angular 2快速入门基础上传angular.io(他们不使用webpack)

  1. 我使用了npm install aws-sdk
  2. 我在index.html中添加了这个:
  3. 这是组件中的代码:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    
    android:id="@+id/toast_container"
    android:orientation="horizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="16dp"
    >
    
    <RelativeLayout
        android:layout_width="800dp"
        android:layout_height="wrap_content"
        android:background="@drawable/toast_bg">
    <ImageView
        android:id="@+id/toastIcon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:src="@drawable/toast_icon" />
    <TextView
        android:id="@+id/toastMsg"
        android:layout_toRightOf="@+id/toastIcon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="sample toast message"/>
    </RelativeLayout>
    

    }

  4. 以下是Firefox Web控制台中的错误日志:

      

    有一个错误:对象{__zone_symbol__error:错误,fileName:Getter,lineNumber:Getter,columnNumber:Getter,message:Getter,stack:Getter,originalStack:Getter,zoneAwareStack:Getter,toString:createMethodProperty / props [key] .value(),toSource:createMethodProperty / props [key] .value(),7 more ...}

    适用于Chrome:

      

    XMLHttpRequest无法加载http://bucketname.s3.amazonaws.com/categories/imagename.png。对预检请求的响应没有通过访问控制检查:否&#39;访问控制 - 允许 - 来源&#39;标头出现在请求的资源上。起源&#39; localhost:3000&#39;因此不允许访问。响应的HTTP状态代码为400。

    正如我刚刚学习的那样,我正在尝试一个宽松的CORS:

    @Injectable()
    export class S3Service {
    
    private uploadSuccess = true;
    
    private creds = {
        "bucket": "nameOfBucket", 
        "access_key": "accessKey",
        "secret_key": "secretKey",
        "region": "us-east-1"
    }
    
    upload(file: File){
        if (file){
            console.log('verified with file');
        }else{
            console.log('without file');
        }
    console.log('filetype verified as images/png: ', file.type);
    
    AWS.config.update({
        accessKeyId: this.creds.access_key,  
        secretAccessKey: this.creds.secret_key, 
    });
    AWS.config.region = this.creds.region;
    AWS.config.sslEnabled = false;
    
    console.log('aws.s3 is verified to be a function: ', AWS.S3);
    let bucket = new AWS.S3({ params: { Bucket: this.creds.bucket }});
    
    let key = `categories/${file.name}`;
    console.log('verified key is : ', key);
    let params = {Key: key, Body: file, ContentType: file.type, ServerSideEncryption: 'AES256'};
    
    bucket.putObject(params, function (err: Response | any, data: Response) {
        if (err){
            console.log('there is an error: ', err);
        }
        else{
            console.log('there is no error in s3 upload');
        }
    }); 
    

    Angular 1代码成功上传图像。

    请帮助和高级谢谢。

1 个答案:

答案 0 :(得分:3)

S3存储桶上的CORS配置在您描述的方案中看起来很好。

我认为问题出在您的代码产生的端点URL上。我有一个类似的问题,它很混乱,很难追查。

出于某种原因,AWS SDK似乎会根据您用于设置区域信息和/或存储桶名称的方法生成不同的端点URL。如果生成的URL不包含区域信息(您的URL没有),则会导致飞行前请求失败,从而导致浏览器控制台中有关CORS的错误消息(有时可能是预检失败的根源。)

&#39;问题&#39;端点格式:http(s)://<bucketname>.s3.amazonaws.com/<key>
&#39;期望&#39;端点格式:http(s)://s3-<region>.amazonaws.com/<bucketname>/<key>

尝试使用此处提供的组件来验证您的AWS S3配置,访问权限和CORS设置。然后,如果您愿意,可以轻松地将S3特定内容提取到服务中。

以下是步骤:

  1. 确认您运行的是当前版本的AWS开发工具包(自2017-02-08起的2.10.0)。如果您不熟悉如何检查,请打开/node_modules/aws-sdk/dist/aws-sdk.js并查看文件顶部的注释以确定版本。
  2. 将以下组件添加到项目中。我已经成功地对自己的S3实例进行了测试。
  3. 确保更新配置值并专门指定S3存储桶所在的区域。如果您不确定可以使用AWS控制台找到此区域。当然,您还需要使用凭据和S3存储桶名称替换其他配置值。
  4. 为AWS SDK库配置systemjs(请参阅下面的system.config.js)
  5. 将组件添加到模块的声明中(请参阅下面的app.module.ts)
  6. 在AppComponent的模板中引用组件(<s3-upload-test></s3-upload-test>)(请参阅下面的app.component.ts)。
  7. <强> S3-上传-test.component.ts:

    import { Component } from '@angular/core';
    import { Credentials, S3 } from 'aws-sdk';
    
    @Component({
      selector: 's3-upload-test',
      template: `
        <div class="uploadSection">
    
          <hr>
          <h3>S3 File Upload Test</h3>
    
          <div class="subsection">
            <h4>Confirm Endpoint Format:</h4>
            <div class="indent">
              The endpoint should be in the following format <span class="monospace">s3-&lt;region&gt;.amazonaws.com</span>.
              <pre>
                Based on the configuration information you provided:
                    Expect Endpoint: {{expectEndpoint}}
                    Actual Endpoint: {{actualEndpoint}}
              </pre>
            </div>
          </div>
    
          <div class="subsection">
            <h4>Select File:</h4>
            <div class="indent">
              <input type="file" (change)="fileEvent($event)" />
            </div>
          </div>
    
          <div class="subsection">
            <h4>Upload Status/Results:</h4>
            <div class="indent">
              <span class="monospace result">{{uploadStatus}}</span>
            </div>
          </div>
    
          <hr>
        </div>
      `,
      styles: [`
        .uploadSection { font-family: sans-serif; }
        .monospace { font-family: monospace; }
        .subsection { margin-top: 35px;}
        .indent { margin-left: 20px;}
        .result { background-color: lightyellow }
      `]
    })
    export class S3UploadTestComponent {
    
      // Replace the values with your own
      private readonly _awsConfig = {
        accessKeyId: "<your keyId>",
        secretAccessKey: "<your secret>",
        s3BucketRegion: "<your region>", // example: "us-west-2"
        s3BucketName: "<your bucket>"    // example: "mycompany.testbucket"
      }
      private _awsCredentials: Credentials;
      private _s3ClientConfig: S3.ClientConfiguration;
      private _s3: S3;
    
      uploadStatus: string = "(no upload yet)";
      expectEndpoint: string;
      actualEndpoint: string;
    
      constructor() {
        // Create an AWS S3 client
        this._awsCredentials = new Credentials(this._awsConfig.accessKeyId, this._awsConfig.secretAccessKey);
        this._s3ClientConfig = {
          credentials: this._awsCredentials,
          region: this._awsConfig.s3BucketRegion,
          sslEnabled: true
        };
        this._s3 = new S3(this._s3ClientConfig);
    
        // Set the expected and actual endpoints
        var isRegionUSEast :boolean = (this._awsConfig.s3BucketRegion).toLowerCase() == "us-east-1";
        var endpointHost :string = isRegionUSEast ? "s3" : "s3-" + this._awsConfig.s3BucketRegion
        this.expectEndpoint = endpointHost + ".amazonaws.com";
        this.actualEndpoint = this._s3.config.endpoint;
      }
    
      // Event triggered when a file has been specified 
      fileEvent(fileInput: any) {
        this.uploadStatus = "starting upload...";
    
        // get the file to upload
        let file: File = fileInput.target.files[0];
        console.log(file);
    
        // upload file to S3
        let putObjectRequest: S3.PutObjectRequest = {
          Key: 'categories/' + file.name,
          Body: file,
          Bucket: this._awsConfig.s3BucketName,
          ContentType: file.type,
          ServerSideEncryption: "AES256"
        };
    
        // use "that" to be able to reach component properties within the then/catch callback functions
        let that = this;
    
        // upload to S3
        this._s3.upload(putObjectRequest).promise()
          .then(function (response: S3.ManagedUpload.SendData) {
            that.uploadStatus = "Success!\n File URI: " + response.Location;
            // alert("upload successful!");
          })
          .catch(function (err: Error) {
            var errMsg = "";
            errMsg += "upload failed.\n ";
            errMsg += "Error Message: " + err.message + "\n ";
            errMsg += "NOTE: an error message of 'Network Failure' may mean that you have the wrong region or the wrong bucket name.";
            that.uploadStatus = errMsg;
            // alert(errMsg);
          });
      }
    }
    

    systemjs.config.js补充:

    (function (global) {
      System.config({
        ...
        map: {
          ...
          'aws-sdk': 'npm:aws-sdk'
        },
        packages: {
          ...
          'aws-sdk': {
            defaultExtension: 'js',
            main: 'dist/aws-sdk.js',
            format: 'global'
          }
        }
      });
    })(this);
    

    <强> app.module.ts:

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    
    import { AppComponent } from './app.component';
    import { S3UploadTestComponent } from './s3-upload-test.component';
    
    @NgModule({
      imports: [BrowserModule],
      declarations: [
        AppComponent,
        S3UploadTestComponent,
      ],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    <强> app.component.ts:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'my-app',
      template: `
        <h1>Hello {{name}}</h1>
        <s3-upload-test></s3-upload-test>
      `,
    })
    export class AppComponent  { name = 'Angular'; }
    

    AWS S3存储桶CORS配置:
    注意:您可能希望根据安全需要制定更严格的限制

    <?xml version="1.0" encoding="UTF-8"?>
    <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
      <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
      </CORSRule>
    </CORSConfiguration>
    

    AWS IAM政策(附加到用户或群组):
    注意:您几乎肯定希望 使允许的操作更具限制性 ,以满足您的安全需求
    注意:将<your bucketname>替换为适当的值

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "Stmt1485926968000",
                "Effect": "Allow",
                "Action": [
                    "s3:*"
                ],
                "Resource": [
                    "arn:aws:s3:::<your bucketname>/*"
                ]
            }
        ]
    }
    


    如果这不能解决您的问题,请使用Chrome开发工具,然后查看网络&#39;选项卡以查看对S3 API的OPTIONS请求,并使用整个响应更新您的问题。当AWS S3飞行前失败时,它们通常会在响应中提供良好的信息。