欠員が出たということで、穴埋めさせていただきます。
概要
本記事は、AngularでAWS SDK for JavaScriptを利用して、S3にファイルをアップロードするという内容です。
Angular メインですので、AWSサービスの使い方や設定については割愛いたします。ご了承ください。
環境
$ uname -a
Linux ip-10-4-0-188 4.9.62-21.56.amzn1.x86_64 #1 SMP Thu Nov 16 05:37:08 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
$ ng -v
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 1.5.5
Node: 8.9.1
OS: linux x64
Angular: 5.0.0
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router
@angular/cli: 1.5.5
@angular-devkit/build-optimizer: 0.0.35
@angular-devkit/core: 0.0.22
@angular-devkit/schematics: 0.0.41
@ngtools/json-schema: 1.1.0
@ngtools/webpack: 1.8.5
@schematics/angular: 0.1.10
@schematics/schematics: 0.0.10
typescript: 2.4.2
webpack: 3.8.1
package.json
{
"name": "qiita",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^5.0.0",
"@angular/common": "^5.0.0",
"@angular/compiler": "^5.0.0",
"@angular/core": "^5.0.0",
"@angular/forms": "^5.0.0",
"@angular/http": "^5.0.0",
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/router": "^5.0.0",
"core-js": "^2.4.1",
"rxjs": "^5.5.2",
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular/cli": "1.5.5",
"@angular/compiler-cli": "^5.0.0",
"@angular/language-service": "^5.0.0",
"@types/jasmine": "~2.5.53",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "^4.0.1",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~3.2.0",
"tslint": "~5.7.0",
"typescript": "~2.4.2"
}
}
手順
1) AWS SDK for JavaScriptをインストール
$ npm i --save-prod aws-sdk
2) src/app/tsconfig.app.json
を編集
tsconfig.app.json
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "es2015",
- "types": []
+ "types": ["node"]
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
3) S3アップロード用コンポネントを作成
$ ng g component s3-upload
create src/app/s3-upload/s3-upload.component.css (0 bytes)
create src/app/s3-upload/s3-upload.component.html (28 bytes)
create src/app/s3-upload/s3-upload.component.spec.ts (643 bytes)
create src/app/s3-upload/s3-upload.component.ts (280 bytes)
update src/app/app.module.ts (408 bytes)
4) 各種ファイルを編集
src/app/app.component.html
+ <app-s3-upload></app-s3-upload>
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { S3UploadComponent } from './s3-upload/s3-upload.component';
import { S3Service } from './s3-upload/s3.service';
@NgModule({
declarations: [
AppComponent,
S3UploadComponent,
],
imports: [
BrowserModule,
HttpClientModule,
],
providers: [
S3Service,
],
bootstrap: [AppComponent]
})
export class AppModule { }
src/app/s3-upload/s3-upload.html
<div class="form">
<div class="form-group">
<label class="col-xs-2">select file</label>
<div class="col-xs-10">
<input type="file" (change)="upload($event)">
</div>
</div>
</div>
<div class="debug_print">
<p>httpUploadProgress {{ httpUploadProgress | json }}</p>
</div>
src/app/s3-upload/s3-upload.component.ts
import { Component, OnInit } from '@angular/core';
import { S3Service } from './s3.service';
import * as AWS from 'aws-sdk';
@Component({
selector: 'app-s3-upload',
templateUrl: './s3-upload.component.html',
styleUrls: ['./s3-upload.component.css']
})
export class S3UploadComponent implements OnInit
{
public httpUploadProgress: {[name: string]: any} = {
ratio : 0,
style : {
width: '0',
}
};
/**
* @desc constructor
*/
constructor(private s3Service: S3Service)
{
this.s3Service.initialize()
.subscribe((res: boolean) => {
if (! res) {
console.log('S3 cognito init error');
}
})
}
/**
* @desc Angular LifeCycle
*/
ngOnInit()
{
this.s3Service.progress
.subscribe((res: AWS.S3.ManagedUpload.Progress) => {
this.httpUploadProgress.ratio = res.loaded * 100 / res.total;
this.httpUploadProgress.style.width = this.httpUploadProgress.ratio + '%';
});
}
/**
* @desc file upload
*/
public upload(event: Event): void
{
this.httpUploadProgress.ratio = 0;
this.httpUploadProgress.style.width = '0';
this.s3Service.onManagedUpload((<HTMLInputElement>event.target).files[0]);
}
}
src/app/s3-upload/s3.service.ts
import { Injectable, EventEmitter } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import * as AWS from 'aws-sdk';
import { AWS_ENV } from '../../environments/environment';
@Injectable()
export class S3Service
{
private s3: AWS.S3;
public progress: EventEmitter<AWS.S3.ManagedUpload.Progress> = new EventEmitter<AWS.S3.ManagedUpload.Progress>();
constructor(private http: HttpClient) { }
/**
* @desc set CognitoIdentityId
*
* @return string IdentityId: ex) ap-northeast-1:01234567-9abc-df01-2345-6789abcd
*/
public initialize(): Observable<boolean>
{
return this.http.get('/assets/cognito.php')
.map((res: any) => {
// resに対する例外処理を追加する
// ...
// Amazon Cognito 認証情報プロバイダーを初期化します
AWS.config.region = AWS_ENV.region;
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityId: res.results[0].IdentityId,
});
this.s3 = new AWS.S3({
apiVersion: AWS_ENV.s3.apiVersion,
params: {Bucket: AWS_ENV.s3.Bucket},
});
return true;
})
.catch((err: HttpErrorResponse) => {
console.error(err);
return Observable.of(false);
});
}
/**
* @desc AWS.S3
*/
public onManagedUpload(file: File): Promise<AWS.S3.ManagedUpload.SendData>
{
let params: AWS.S3.Types.PutObjectRequest = {
Bucket: AWS_ENV.s3.Bucket,
Key: file.name,
Body: file,
};
let options: AWS.S3.ManagedUpload.ManagedUploadOptions = {
params: params,
partSize: 64*1024*1024,
};
let handler: AWS.S3.ManagedUpload = new AWS.S3.ManagedUpload(options);
handler.on('httpUploadProgress', this._httpUploadProgress.bind(this));
handler.send(this._send.bind(this));
return handler.promise();
}
/**
* @desc progress
*/
private _httpUploadProgress(progress: AWS.S3.ManagedUpload.Progress): void
{
this.progress.emit(progress);
}
/**
* @desc send
*/
private _send(err: AWS.AWSError, data: AWS.S3.ManagedUpload.SendData): void
{
console.log('send()', err, data);
}
}
src/environments/environment.ts
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
production: false
};
export const AWS_ENV = {
region: 'YOUR_REGION',
s3: {
apiVersion: 'YOUR_VERSION',
Bucket: 'YOUR_BACKET',
},
};
5) ビルド&実行&確認
$ aws s3 ls s3://YOUR_BACKET/
2017-12-10 08:13:54 10485760 10MB.bin
解説
AWS SDKを使ってファイルをアップロードするには、**PutObject()を使うのが手っ取り早いですが
JSからファイルをアップロードするときには、UI/UXの観点からプログレスを表示してあげるのがよいので
それに対応したメソッドである、ManagedUpload()**を利用しました。
5.0.0 では、zoneを意識することなく、プログレスがきちんとレンダリングされましたので
プログレスバーの実装は容易に行なえます。
以上、穴埋め記事でした。
7日目は、@segawm さんです。