Pretty image uploader with PHP server


#1

I spent many hours trying to upload a image reading again and again all the posts about without much success. And I got quite discouraged by the fact that none of the various posts illustrated a full working example. Finally I worked it out by myself. I hope that this code will help others Fuse beginners.

Add Fuse.CameraRoll to your .unoproj file (run uno clean if necessary):

{
  "RootNamespace":"",
  "Packages": [
    "Fuse",
    "FuseJS",
    "Fuse.CameraRoll"
  ],
  "Includes": [
    "*"
  ]
}

MainView.ux

<App>
    <JavaScript>
        var Observable      = require("FuseJS/Observable");
        var cameraRoll      = require("FuseJS/CameraRoll");
        var ImageTools      = require('FuseJS/ImageTools');

        var uploadReport    = Observable();
        var selectedImage   = Observable();

        function uploadImage() {
            busy.activate();
            ImageTools.getBase64FromImage(selectedImage.value).then(function(base64Image) {                
                return fetch('http://your_server/upload.php', { 
                    method: "POST",
                    body: base64Image 
                }).then(function(response) {
                    busy.deactivate();
                    if (response.ok) {
                        return response.json();
                    } else {    
                        throw Error(response.status);
                    }
                }).then(function(json) {
                    uploadReport.value = json.status;       //For debug purposes: JSON.stringify(json)
                });
            });
        };

        function browseImage() {
            cameraRoll.getImage().then(function(image) {
                selectedImage.value = image;
            }, function(error) {
                console.log(error);
            });
        };

         module.exports = {
            selectedImage:      selectedImage,
            uploadImage:        uploadImage,
            browseImage:        browseImage,
            uploadReport:       uploadReport
        };

    </JavaScript>

    <ClientPanel>
        
        <!-- Show loading indicator -->
        <WhileBusy>
            <Change loadingPanel.Opacity="1" Duration=".4" />
        </WhileBusy>
        
        <!-- Hide loading indicator (default). Will be set as visible when the fetch call sets Busy as active -->
        <MyLoadingIndicator ux:Name="loadingPanel" Opacity="0" ThemeColor="#fff" />

        <!-- Set busy not active (default). Will be set as active by the fetch call -->
        <Busy IsActive="false" ux:Name="busy"/>

        <StackPanel Alignment="Center">
            <Image File="{selectedImage}" Height="240" Width="360"/>
            <Text TextAlignment="Center" Value="{uploadReport}" Margin="0,40,0,0"/>
        </StackPanel>
    
        <DockPanel Dock="Bottom" Margin="12,0,12,12">
            <MyButton Dock="Bottom" Text="Upload" Clicked="{uploadImage}" NormalStateColor="#0070C0" ClickedStateColor="#004E87" Margin="0,12,0,0"/>
            <MyButton Dock="Bottom" Text="Browse gallery" Clicked="{browseImage}" NormalStateColor="#0070C0" ClickedStateColor="#004E87" />
        </DockPanel>



        <!-- ========================================================================== -->
        <Panel ux:Class="MyButton" HitTestMode="LocalBounds" Color="{ReadProperty NormalStateColor}">
            <string ux:Property="Text" />
            <float4 ux:Property="NormalStateColor" />
            <float4 ux:Property="ClickedStateColor" />
            <Text Value="{ReadProperty Text}" Color="#fff" TextAlignment="Center" TextWrapping="Wrap" Margin="28,16,28,16" />
            <WhilePressed>
                <Change this.Color="{ReadProperty ClickedStateColor}" Duration="0.05" DurationBack=".2" />
            </WhilePressed>
        </Panel>

        <Panel ux:Class="MyLoadingIndicator" ThemeColor="#1565C0">
            <float4 ux:Property="ThemeColor" />
            <Circle ux:Name="rotatingStroke" Width="50" Height="50" StartAngleDegrees="-45" EndAngleDegrees="45">
                <Stroke Width="2" Color="{ReadProperty ThemeColor}" />
            </Circle>
            <WhileFalse>
                <Spin Target="rotatingStroke" Frequency="1" />
            </WhileFalse>
        </Panel>

    </ClientPanel>

</App>

upload.php

<?php
    if ($_SERVER["REQUEST_METHOD"] == "POST") {
    
        /*
        php://input is a read-only stream that allows you to read raw data from the request body. 
        In the case of POST requests, it is preferable to use php://input instead of $HTTP_RAW_POST_DATA 
        as it does not depend on special php.ini directives.
        
        php://input is not available for requests specifying a
        Content-Type: multipart/form-data header (enctype="multipart/form-data" in HTML forms). 
        This results from PHP already having parsed the form data into the $_POST superglobal.
        */
        $getBody = file_get_contents('php://input');
        
        // Decode the base64 string sent from the app
        $base64image = base64_decode($getBody); 
        
        // Create a new image from the image stream in the string
        $source = imagecreatefromstring($base64image);
        
        // Save the image to a file 
        // ('75' will keep the same file size of the original. Values above '75' will increase the file size)
        $imageSave = imagejpeg($source, "image.jpg", 75);
        
        // Free the memory
        imagedestroy($source);
        
        // If the image has been saved report back to the app
        if ($imageSave) {
            
            // This key 'status' is the Fuse 'json.status' key
            // The string 'Server reports...' is the value of the above Fuse key ('uploadReport.value')
            $result = array('status' => 'Server reports: image received and saved');
            
            echo json_encode($result);
        }
        
    }

?>

Please share any improvement. One improvement could be detect the type of image (this code saves all images as JPG because there is no way to get the file type from a Base64 image. I tried the PHP FILEINFO_MIME_TYPE but it didn’t always report the correct image type).