DEV Community

Cover image for How to upload an image to AWS S3 pre-assign URL with background-http.
vanishdark
vanishdark

Posted on

How to upload an image to AWS S3 pre-assign URL with background-http.

Recently I had a task to upload an image to an s3 pre-assign URL in my project using the framework Nativescript.

So what was the challenge?

Send an ImageSource using the background-http.

My first attempts weren't successful because I was trying to send the base64 data or even the ImageSource himself which at the time seemed a nice idea but wasn't. All the data were corrupted or not even sent.
So after some attempts and a few hours of trying and failing, I finally got the right way to do it.
I must thank the guys from Nativescript, wwwalkerrun and triniwiz, for guiding me in the right way.

Installing the Packages

In order to upload data to the S3 we need background-http to help us here.

ns plugin add @nativescript/background-http
Enter fullscreen mode Exit fullscreen mode

If you want to check the plugin you can do it here.

In your main.ts or main.js or where your application starts you need to initialise the background plugin. To do so

import { init } from '@nativescript/background-http';
init();
Enter fullscreen mode Exit fullscreen mode

We are importing the init function and initialising it in that application start file.

Creating the AWS Service

To make my code clean I create a file called AWSService.ts which will simplify the way we will use the service.

import { Http, HttpRequestOptions, HttpResponse } from '@nativescript/core';
import { Request, Session } from '@nativescript/background-http';
import { HttpService } from '~/services/generic/Http.service';

export class AWSService {
    public get session(): Session {
        return this._session;
    }
    public set session(value: Session) {
        this._session = value;
    }

    private _session: Session;

    constructor() {
        this.session = this.initSession();
    }
    initSession(): Session {
        return bghttp.session('image-upload'); // you can rename this image-upload to watever you like.
    }

    async requestUrl(name: string): Promise<HttpResponse> {
// This will be your implementation to request the pre-assigned url
    }

    async uploadImage(url: string, file: any, name?: string, type?: string): Promise<string> {
        try {
            if (!type) type = 'image/jpg';
            if (!name) name = 'filename.jpg';
            const request: Request = {
                url,
                method: 'PUT',
                headers: {
                    'Content-Type': type,
                    reportProgress: true,
                },
                description: 'UpladingFile' + file,
            };
            this.initTask(file, request, name, type);
        } catch (e) {
            console.log(e);
            return '';
        }
    }
    initTask(file: any, request: Request, name: string, type: string): void {
        try {
            const params = [{ name, filename: file, mimeType: type }];
            const task = this.session.uploadFile(file, request);
            task.on('progress', e => console.log('progress', e.currentBytes));
            task.on('responded', e => console.log('responded', e.responseCode));
            task.on('error', e => console.log('error', e.responseCode));
            task.on('complete', e => {
                console.log('complete', e);
                const toast = new Toasty({
                    text: 'Image uploaded',
                    position: ToastPosition.BOTTOM,
                });
                toast.duration = ToastDuration.SHORT;
                toast.show();
            });
        } catch (e) {
            console.log('initTaks Erro -', e);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Breaking the code in parts let's start with the initSession
We are creating a new session when we create a new instance of AWSService. And the initSession() creates a new session of bghttp.

constructor(){
   this.session = this.initSession();
}
Enter fullscreen mode Exit fullscreen mode
initSession(): Session {
        return bghttp.session('some name);
    }
Enter fullscreen mode Exit fullscreen mode

The uploadImage function takes the pre-assign URL, the path of the file, the name of the file and the mimeType. We grab those create a Request object and init the task.

async sendImage(url: string, file: any, name?: string, type?: string): Promise<string> {
        try {
            if (!type) type = 'image/jpg';
            if (!name) name = 'filename.jpg';
            const request: Request = {
                url,
                method: 'PUT',
                headers: {
                    'Content-Type': type,
                    reportProgress: true,
                },
                description: 'UpladingFile' + file,
            };
            this.initTask(file, request, name, type);
        } catch (e) {
            console.log(e);
            return '';
        }
    }
Enter fullscreen mode Exit fullscreen mode

And last but not least our initTask function initialise our upload process. This takes our file path and the request created by the sendImage function. I add the events from the task but I'm not doing anything right now with him, but you can track the progress, the result and if some error occurs. If everything works perfectly your respond or complete will return a result. To be sure navigate to your AWS s3 bucket and check if the file is not corrupted. If not Good Job your service is running perfectly.

initTask(file: any, request: Request): void {
        try {
            const task = this.session.uploadFile(file, request);
            task.on('progress', e => console.log('progress', e.currentBytes));
            task.on('responded', e => console.log('responded', e.responseCode));
            task.on('error', e => console.log('error', e.responseCode));
            task.on('complete', e => console.log('complete', e));
        } catch (e) {
            console.log('initTaks Erro -', e);
        }
    }
Enter fullscreen mode Exit fullscreen mode

Using the AWSService

Using the service is really simple you just need to initialise and call the functions. Of course we need the image source the example below is using Image Data from another package that let you draw a paint.

...
const amazonService = new AWSService();
// Grab the preassign url
const preAssignURL = await amazonService.requestUrl('image.png');
// data returned from the draw paint
const data: UIImage | android.graphics.Bitmap = await draw.getDrawing();
// Creating a new ImageSource
const source = new ImageSource(newImage);
const path = `${knownFolders.documents().path}/image.png`;
// Saving the image source to a file;
source.saveToFile(path, 'png');
await amazonService.sendImage(preAssignURL,path, 'image/png');
Enter fullscreen mode Exit fullscreen mode

If you don't want the keep the file in the device you can simply delete by adding the follow code

const file = File.fromPath(path);
file.remove();
Enter fullscreen mode Exit fullscreen mode

That's it. If your file is in the S3 bucket and is not corrupted everything works as we want.

If you want the check the full code you can go to my gist or if you prefer here is the individual files

Top comments (0)