upload image from Angular to aws s3 error: the image cannot be displayed because it contains errors

Issue

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

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 '';
    });
  }

Answered By – Mor Sagmon

Answer Checked By – Mildred Charles (AngularFixing Admin)

Leave a Reply

Your email address will not be published.