I'm trying to capture an image with the Android camera and upload it to an AWS S3 bucket. Using Angular 13 Ionic 6 and the Capacitor camera.
Finally I got the image to upload to S3, but when attempting to view it in the browser, I get an error:
image ... cannot be displayed because it contains errors
The image size shown in the S3 bucket is always 48.5 Kb. I found a couple of reports with similar error messages but none helped.
In my signup.component.ts:
async uploadProfilePhotoToAws(txKey: string, fileName: string): Promise<string> {
let image = await this.getBase64ImageFromUrl(this.profilePhotoUrl.split(',')[1]);
var data = {
Key: 'test-key-profile-image', //txKey,
name: fileName '.jpg', //Value: profiles/09720004658354.jpg
value: image,
ContentEncoding: 'base64',
ContentType: 'image/jpeg',
type: 'image/jpeg'
};
console.log('AWS data payload: ', JSON.stringify(data));
//Upload profile image to AWS s3
return this._aws.uploadDataFile(data).then(res => {
if (res) {
console.log('aws profile file returned: ', res);
return res;
}
}).catch(err => {
console.log('AWS profile upload error: ', err);
return '';
});
}
And also:
async getBase64ImageFromUrl(imageUrl) {
var res = await fetch(imageUrl);
var blob = await res.blob();
return new Promise((resolve, reject) => {
var reader = new FileReader();
reader.addEventListener("load", function () {
resolve(reader.result);
}, false);
reader.onerror = () => {
return reject(this);
};
reader.readAsDataURL(blob);
})
}
My add-photo.component.ts - serving the above signup component:
import { Component, EventEmitter, OnInit, Output, Input } from '@angular/core';
import { PhotoService } from '../../services/photo.service';
import { CameraDirection, CameraResultType } from '@capacitor/camera';
@Component({
selector: 'app-add-photo',
templateUrl: './add-photo.component.html',
styleUrls: ['./add-photo.component.scss'],
})
export class AddPhotoComponent implements OnInit {
@Output('photo') photo = new EventEmitter<string>();
@Input() receivedPhotoPath: string;
photoPath: string = "/assets/media/avatar.svg";
constructor(public photoService: PhotoService) { }
ngOnInit() {
console.log('OnInit Received Photo Path: ', this.receivedPhotoPath);
if (this.receivedPhotoPath!='') {
this.photoPath = this.receivedPhotoPath;
}
}
capturePhoto() {
console.log('add-photo Component about to call the capturePhoto service');
this.photoService.capturePhoto(CameraDirection.Front, CameraResultType.Uri).then((photoResult: string) => {
console.log('Returned from capturePhoto: ' JSON.stringify(photoResult));
this.photoPath = photoResult;
this.photo.emit(photoResult);
}).catch((error) => {
console.log('Failed profile picture capture. Error: ' error.message);
});
}
}
And the photo.service.ts - serving the above add-photo.component:
import { Injectable } from '@angular/core';
import { Camera, CameraDirection, CameraResultType, CameraSource, Photo } from '@capacitor/camera';
@Injectable({
providedIn: 'root'
})
export class PhotoService {
public photoUrl: string;
constructor() { }
public async capturePhoto(direction: CameraDirection = CameraDirection.Rear, resultType: CameraResultType = CameraResultType.Uri): Promise<string> {
const capturedPhoto = await Camera.getPhoto({
resultType: resultType,
source: CameraSource.Prompt,
direction: direction,
quality: 100
});
this.photoUrl = capturedPhoto.webPath;
return this.photoUrl;
}
}
The aws-file-upload.service.ts:
import { Injectable } from '@angular/core';
import * as AWS from 'aws-sdk/global';
import * as S3 from 'aws-sdk/clients/s3';
import { environment } from '../../environments/environment';
import { PromiseResult } from 'aws-sdk/lib/request';
import { PutObjectOutput } from 'aws-sdk/clients/s3';
import { AWSError } from 'aws-sdk/global';
@Injectable({
providedIn: 'root'
})
export class AwsFileUploadService {
constructor() { }
uploadDataFile(data: any) {
const contentType = data.type;
const bucket = new S3({
accessKeyId: environment.awsAccessKey,
secretAccessKey: environment.awsSecret,
region: environment.awsRegion
});
const params = {
Bucket: environment.awsBucket,
Key: data.name, //manipulate filename here before uploading
Body: data.value,
ContentEncoding: 'base64',
ContentType: contentType
};
var putObjectPromise = bucket.putObject(params).promise();
return putObjectPromise.then(function(data) {
console.log('succesfully uploaded the image! ' JSON.stringify(data));
return data;
}).catch(function(err) {
console.log(err);
return err;
});
}
}
Please help me in identifying what am I doing wrong. Thank you!
CodePudding user response:
The solution was to use Buffer while preparing the image payload to AWS S3:
async uploadProfilePhotoToAws(txKey: string, fileName: string): Promise<string> {
console.log('Photo URL Before passed to Base64 transformation: ', this.profilePhotoUrl);
let image = await this.getBase64ImageFromUrl(this.profilePhotoUrl); //Image saved corrupt in S3. Check why
let buf = Buffer.from(image.toString().replace(/^data:image\/\w ;base64,/, ""), "base64");
var data = {
Key: txKey,
name: fileName '.jpg',
value: buf,
ContentEncoding: 'base64',
ContentType: 'image/jpeg',
type: 'image/jpeg'
};
//Upload profile image to AWS s3
return this._aws.uploadDataFile(data).then(res => {
if (res) {
console.log('aws profile file returned: ', res);
return res;
}
}).catch(err => {
console.log('AWS profile upload error: ', err);
return '';
});
}