How to upload an image to AWS S3 pre-assign URL with background-http.
vanishdark
Posted on December 4, 2022
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
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();
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);
}
}
}
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();
}
initSession(): Session {
return bghttp.session('some name);
}
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 '';
}
}
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);
}
}
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');
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();
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
Posted on December 4, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.