'upload image from Angular to aws s3 error: the image cannot be displayed because it contains errors
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!
Solution 1:[1]
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 '';
});
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Mor Sagmon |