您在 S3 存储桶上的 CORS 配置看起来适合您描述的场景。
我认为问题出在您的代码产生的端点 URL 上。我有一个类似的问题,它令人困惑,而且很难追踪。
由于某种原因,AWS 开发工具包生成端点 URL 的方式似乎有所不同,具体取决于您用于设置区域信息和/或存储桶名称的方法。当生成的 URL 不包含区域信息(您的不包含)时,它会导致预检请求失败,从而导致浏览器控制台中出现有关 CORS 的误导性错误消息(这有时可能是预检失败的根源) )。
“问题”端点格式:http(s)://<bucketname>.s3.amazonaws.com/<key>
“所需”端点格式:http(s)://s3-<region>.amazonaws.com/<bucketname>/<key>
尝试使用我在此处提供的组件来验证您的 AWS S3 配置、访问和 CORS 设置。然后,如果您愿意,您可以轻松地将 S3 特定内容提取到服务中。
步骤如下:
- 确认您正在运行当前版本的 AWS 开发工具包(2017-02-08 为 2.10.0)。万一您不熟悉如何检查此内容,请打开 /node_modules/aws-sdk/dist/aws-sdk.js 并查看文件顶部的注释以确定版本。
- 将以下组件添加到您的项目中。我已经成功地针对我自己的 S3 实例进行了测试。
- 确保更新配置值并明确指定 S3 存储桶所在的区域。如果您不确定,可以使用 AWS 控制台找到此信息。当然,您还需要使用您的凭据和 S3 存储桶名称替换其他配置值。
- 为 AWS 开发工具包库配置 systemjs(参见下面的 system.config.js)
- 将组件添加到模块的声明中(请参阅下面的 app.module.ts)
- 在 AppComponent 的模板中引用组件 (
<s3-upload-test></s3-upload-test>)(请参阅下面的 app.component.ts)。
s3-upload-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-<region>.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 开发工具并查看“网络”选项卡以查看对 S3 API 的 OPTIONS 请求并使用整个响应更新您的问题。当 AWS S3 预检失败时,它们通常会在响应中提供良好的信息。