Back-End

1 set, 2017

Tirando fotos com uma aplicação ionic2 e enviando-as para o bucket S3 com a Cloud Foundry da SAP usando Silex e Lumen

Publicidade

Hoje eu quero brincar com um experimento. Quando eu trabalho com aplicativos móveis, normalmente uso backends iônicos e on-premise. Hoje eu quero brincar com backends baseados em nuvem. Neste pequeno experimento, quero usar um aplicativo ionic2 para tirar fotos e enviá-las para um bucket S3. Vamos começar.

Primeiro, criei uma simples aplicação ionic2. É uma aplicação muito simples. Apenas uma página com um botão para disparar a câmera do dispositivo.

 

<ion-header>
    <ion-navbar>
        <ion-title>
            Photo
        </ion-title>
    </ion-navbar>
</ion-header>
 
<ion-content padding>
    <ion-fab bottom right>
        <button ion-fab (click)="takePicture()">
            <ion-icon  name="camera"></ion-icon>
        </button>
    </ion-fab>
</ion-content>

 

O controlador usa @ionic-native/camera para tirar fotos e depois usamos @ionic-native/transfer para carregá-los no backend.

 

import {Component} from '@angular/core';
import {Camera, CameraOptions} from '@ionic-native/camera';
import {Transfer, FileUploadOptions, TransferObject} from '@ionic-native/transfer';
import {ToastController} from 'ionic-angular';
import {LoadingController} from 'ionic-angular';
 
@Component({
    selector: 'page-home',
    templateUrl: 'home.html'
})
export class HomePage {
    constructor(private transfer: Transfer,
                private camera: Camera,
                public toastCtrl: ToastController,
                public loading: LoadingController) {
    }
 
    takePicture() {
        const options: CameraOptions = {
            quality: 100,
            destinationType: this.camera.DestinationType.FILE_URI,
            sourceType: this.camera.PictureSourceType.CAMERA,
            encodingType: this.camera.EncodingType.JPEG,
            targetWidth: 1000,
            targetHeight: 1000,
            saveToPhotoAlbum: false,
            correctOrientation: true
        };
 
        this.camera.getPicture(options).then((uri) => {
            const fileTransfer: TransferObject = this.transfer.create();
 
            let options: FileUploadOptions = {
                fileKey: 'file',
                fileName: uri.substr(uri.lastIndexOf('/') + 1),
                chunkedMode: true,
                headers: {
                    Connection: "close"
                },
                params: {
                    metadata: {foo: 'bar'},
                    token: 'mySuperSecretToken'
                }
            };
 
            let loader = this.loading.create({
                content: 'Uploading ...',
            });
 
            loader.present().then(() => {
                let s3UploadUri = 'https://myApp.cfapps.eu10.hana.ondemand.com/upload';
                fileTransfer.upload(uri, s3UploadUri, options).then((data) => {
                    let message;
                    let response = JSON.parse(data.response);
                    if (response['status']) {
                        message = 'Picture uploaded to S3: ' + response['key']
                    } else {
                        message = 'Error Uploading to S3: ' + response['error']
                    }
                    loader.dismiss();
                    let toast = this.toastCtrl.create({
                        message: message,
                        duration: 3000
                    });
                    toast.present();
                }, (err) => {
                    loader.dismiss();
                    let toast = this.toastCtrl.create({
                        message: "Error",
                        duration: 3000
                    });
                    toast.present();
                });
            });
        });
    }
}

 

Agora, vamos trabalhar com o backend. Na próxima vez, usarei o JavaScript AWS SDK para fazer upload de fotos diretamente do aplicativo móvel (sem backend), mas hoje usaremos um backend. Atualmente, estou envolvido com os projetos da plataforma SAP Cloud, então usaremos o tenant Cloud Foundry da SAP (usando uma conta gratuita). Nesse tenant, criaremos uma aplicação PHP usando o PHP buildpack com nginx.

aplicações:
– nome: myApp
caminho: .
memória: 128MB
buildpack: php_buildpack

A aplicação PHP é uma aplicação Silex simples para lidar com os carregamentos de arquivo e postar as imagens no S3 usando o SDK AWS oficial para PHP (baseado em Guzzle).

 

use Symfony\Component\HttpFoundation\Request;
use Silex\Application;
use Aws\S3\S3Client;
 
require 'vendor/autoload.php';
 
$app = new Application([
    'debug'        => false,
    'aws.config'   => [
        'debug'       => false,
        'version'     => 'latest',
        'region'      => 'eu-west-1',
        'credentials' => [
            'key'    => $_ENV['s3key'],
            'secret' => $_ENV['s3secret'],
        ],
    ],
]);
 
$app['aws'] = function () use ($app) {
    return new S3Client($app['aws.config']);
};
 
$app->post('/upload', function (Request $request, Application $app) {
    $metadata = json_decode($request->get('metadata'), true);
    $token    = $request->get('token');
 
    if ($token === $_ENV['token']) {
        $fileName = $_FILES['file']['name'];
        $fileType = $_FILES['file']['type'];
        $tmpName  = $_FILES['file']['tmp_name'];
 
        /** @var \Aws\S3\S3Client $s3 */
        $s3 = $app['aws'];
        try {
            $key = date('YmdHis') . "_" . $fileName;
            $s3->putObject([
                'Bucket'      => $_ENV['s3bucket'],
                'Key'         => $key,
                'SourceFile'  => $tmpName,
                'ContentType' => $fileType,
                'Metadata'    => $metadata,
            ]);
            unlink($tmpName);
 
            return $app->json([
                'status' => true,
                'key'    => $key,
            ]);
        } catch (Aws\S3\Exception\S3Exception $e) {
            return $app->json([
                'status' => false,
                'error'  => $e->getMessage(),
            ]);
        }
    } else {
        return $app->json([
            'status' => false,
            'error'  => "Token error",
        ]);
    }
});
 
$app->run();

 

Eu só queria um protótipo simples (e que funcionasse), o suficiente para um hacking de domingo pela manhã.

UPDATE

Eu fiz este texto há algumas semanas, mas algo mudou. Silex está morto. Então, como um exercício, eu migrarei o aplicativo Silex atual para Lumen (um protótipo rápido).

Esta é a aplicação principal.

 

use App\Http\Middleware;
use Aws\S3\S3Client;
use Illuminate\Http\Request;
use Laravel\Lumen\Application;
 
require 'vendor/autoload.php';
 
(new Dotenv\Dotenv(__DIR__ . "/../env"))->load();
 
$app = new Application();
 
$app->routeMiddleware([
    'auth' => Middleware\AuthMiddleware::class,
]);
 
$app->register(App\Providers\S3ServiceProvider::class);
 
$app->group(['middleware' => 'auth'], function (Application $app) {
    $app->post('/upload', function (Request $request, Application $app, S3Client $s3) {
        $metadata = json_decode($request->get('metadata'), true);
        $fileName = $_FILES['file']['name'];
        $fileType = $_FILES['file']['type'];
        $tmpName  = $_FILES['file']['tmp_name'];
 
        try {
            $key = date('YmdHis') . "_" . $fileName;
            $s3->putObject([
                'Bucket'      => getenv('s3bucket'),
                'Key'         => $key,
                'SourceFile'  => $tmpName,
                'ContentType' => $fileType,
                'Metadata'    => $metadata,
            ]);
            unlink($tmpName);
 
            return response()->json([
                'status' => true,
                'key'    => $key,
            ]);
        } catch (Aws\S3\Exception\S3Exception $e) {
            return response()->json([
                'status' => false,
                'error'  => $e->getMessage(),
            ]);
        }
    });
});
 
$app->run();

 


Provavelmente podemos encontrar um provedor S3 Service, mas criei um simples para este exemplo.

 

namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
use Aws\S3\S3Client;
 
class S3ServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind(S3Client::class, function ($app) {
            $conf = [
                'debug'       => false,
                'version'     => getenv('AWS_VERSION'),
                'region'      => getenv('AWS_REGION'),
                'credentials' => [
                    'key'    => getenv('s3key'),
                    'secret' => getenv('s3secret'),
                ],
            ];
 
            return new S3Client($conf);
        });
    }
}

 

E também estou usando um middleware para a autenticação.

 

namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
 
class AuthMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        $token = $request->get('token');
        if ($token === getenv('token')) {
            return response('Admin Login', 401);
        }
 
        return $next($request);
    }
}

 

Ok. Vou publicar esse artigo em breve. Pelo menos antes de o Lumen também estar morto, e eu precisar atualizar este texto novamente.

O projeto completo (aplicativo móvel e os dois backends) estão no meu GitHub.

 

***

 

Gonzalo Ayuso faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela Redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: https://gonzalo123.com/2017/08/21/taking-photos-with-an-ionic2-application-and-upload-them-to-s3-bucket-with-saps-cloud-foundry-using-silex-and-lumen/