fetch getImageFromBase64 from json example, please

I’m an absolute beginner. I’m fetching data from an API that outputs in JSON format:

<JavaScript>
    var Observable = require("FuseJS/Observable");
    var ImageTools = require("FuseJS/ImageTools");

    var data = Observable();
    var imagePath = Observable();

    fetch('http://www.tsn.it/api/tsn.php?category=tutti')
         .then(function(response) { 
              return response.json(); 
         })
         .then(function(responseObject) { 
              data.value = responseObject;
     });

        module.exports = {
            data: data,
        };
 </JavaScript>

<Each Items="{data.eventi}">
     <DockPanel>
         <Text Value="{title}" TextAlignment="Center" Alignment="Center" />
     </DockPanel>	
</Each>

The JSON has some Base64 encoded images that I would like to display. The Base64 string is saved in the JSON key imageencoded.

  1. I did include Fuse.ImageTools in my .unoproj file

  2. I know that getImageFromBase64 returns a promise and an image may be displayed like this:

    var base64Image = some_long_base64_string;
    ImageTools.getImageFromBase64(base64Image)
    .then(function(image) {
    imagePath.value = image.path;
    });

but PLEASE explain me how do I retrieve the base64 string from the JSON, pass it to getImageFromBase64 and then display it.

Doesn’t this example from the documentation work?

Hi Anders ,
I got the second code snippet I mention above exactly from that example, but I still don’t understand how to pass the JSON data to getImageFromBase64. To make it short, basically I don’t understand how to add that code to into the JSON fetching part so that I have this:

var base64Image = how_do_I_put_here_my_json_data_value?

Many thanks

I don’t know how your http response object look like, but it will be something like this:

fetch('http://www.tsn.it/api/tsn.php?category=tutti').then(function(response) { 
	return response.json();
}).then(function(responseObject) { 
	var base64Image = responseObject.some_long_base64_string;
	return ImageTools.getImageFromBase64(base64Image);
}).then(function(image) {
	imagePath.value = image.path;
}).catch(function(e) {
	console.log("An error occured!");
});```

Oh yeah! Thank you for pointing me in the right direction.

I’m still getting mad with fetching and displaying a JSON object with base64 encode images in it. I understood how to display JSON data, I understood how to display base64 encoded images, but I don’t know how to put the two things together as I keep getting displayed always the LAST image found in the JSON data which is being replicated for every JSON entry. On the other hand the other JSON entries (title, description) are shown correctly.

My JSON is structured like this:

{
    "eventi": [
        {
            "title": "Lorem ipsum",
            "description": "Dolor sit amet...",
            "imageencoded": "iVBORw0KGgoAAAANSUhEUgAAACkAAAAlCAYAAADfosCNAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACUElEQVRYhb2YvUtbURjGH0IGCVJEOpRQQociIg7FUZpOTZcM6ZSpRjq46aCELlUyuWWwpQ39EDJI/wAlQ2nIoCAlU4cSgi5xUuLShMY0xWh8zw0RCffce+45ee8DDzfc8/H+8p6PvAQj0Dy5x+hfo4DcY4ZcMgWcJl8xAjbJ4wFDyDWy6RxO2iG3TCZ4QP4PsC71LAy1yQz4cxAoqAk4Tl62awgGg0gkEojFYgiHw2g0GqjVashkMl5jfIShVmHz7QOBQK9QKPSGVa1WvWbxD3lsEEwnk2JM2q4hlUohHo9bn7vdLsrlMjqdDiqVitcY4sB0YKBXkGQgn8/fZi8ajZrsxykYqiqbvFgsWoDNZtMEcH84oNc77gX6F7itaE9aT7HEBvow/MLrntywe5lO97doJBKxnqFQ6PZdu91GLpdTnf+cvAsDzUGyRE6q1+telnrTLrCXTK7LGrLZrPVMJpNWNu9mr9VS/lW7Jn+FgR5DoZAolUo62Ru4KAuuenC4Cwmhd7IGlcD3ya/Bq1Pyd1mjCuQK7vxEMWmb3JU1ukGGICkkRigB99mpgxvkInkSvBLLfOrUwQlSXE9vwC/XkswJ8iX5EXh1Qv7h1skJ8i34JS7va7dOMshn5CfglTgw2yodZZAb4JcoJM5VOtpBzpCfg1/vVTvaQfqxF4/JB7qDH5IvoV9Vq3oVBtryAfAfeUIXUAz86wPkNxho3QdA4ae6gKLKOfMB8LcO3OB0L6D/BxS3PukOFKBH4M/iBfmeLmQC/uzFLzDQoU+Qc3p4wA0klk08LjmMHgAAAABJRU5ErkJggg=="
        }
}

And I arranged this from your News feed example (https://www.fusetools.com/examples/news-feed):

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

        var datas = Observable();
        var imagePath = Observable();

        fetch("http://www.triestenascosta.it/api/tsn.php?category=tutti")
        .then(function(response) {
        	return response.json(); 
        })
        .then(function(responseObject) {
            datas.value = responseObject;
            
            var items = [];
            for (var i in datas.value.eventi) {
                items.push(datas.value.eventi[i]);

                return ImageTools.getImageFromBase64(datas.value.eventi[i].imageencoded);
            }

        }).then(function(image) {
            imagePath.value = image.path;
        });    

        module.exports = {
            data: datas,
            image: imagePath
        };
    </JavaScript>

    <Panel ux:Class="NewsItem">
        <StackPanel ItemSpacing="10" Margin="15">
            <Text Value="{title}" TextWrapping="Wrap" FontSize="20" />
            <DockPanel>
                <Text Value="{description}" TextWrapping="Wrap" FontSize="13" />
                <Panel Dock="Left">
                    <Image File="{image}" Width="80" Margin="0,0,10,0" Alignment="TopLeft" />
                </Panel>
            </DockPanel>
        </StackPanel>
        <Rectangle Color="#FFF" />
    </Panel>

    <ClientPanel>
        <Panel Dock="Top" Height="46">
            <Text Value="NEWS" Color="#ECEFF1" FontSize="28" Alignment="Center" />
            <Rectangle Color="#37474F" />
            <Shadow />
        </Panel>
        <ScrollView>
            <StackPanel ItemSpacing="1">
                <Each Items="{data.eventi}">
                    <NewsItem />
                </Each>
            </StackPanel>
        </ScrollView>
    </ClientPanel>
</App>

The code should look like this, but there is actually a bug with this code. So it will overwrite the path all the time. There is already an issue here about it. I will see what we can do to get this fixed soon. You can also look into this thread for an alternate solution:

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

        var datas = Observable();

        fetch("http://www.triestenascosta.it/api/tsn.php?category=tutti").then(function(response) {
            return response.json(); 
        }).then(function(json) {
            json.eventi.forEach(function(item) {
                ImageTools.getImageFromBase64(item.imageencoded).then(function(image) {
                    item.image = image.path;
                    datas.add(item);
                }).catch(function(e) {
                    console.log("error getting image for " + item.title + " " + e);
                });
            });
        }).catch(function(e) {
            console.log("error " + e);
        });

        module.exports = {
            data: datas
        };
    </JavaScript>

    <Panel ux:Class="NewsItem">
        <StackPanel ItemSpacing="10" Margin="15">
            <Text Value="{title}" TextWrapping="Wrap" FontSize="20" />
            <DockPanel>
                <Text Value="{description}" TextWrapping="Wrap" FontSize="13" />
                <Panel Dock="Left">
                    <Image File="{image}" Width="80" Margin="0,0,10,0" Alignment="TopLeft" CachingMode="Never" />
                </Panel>
            </DockPanel>
        </StackPanel>
        <Rectangle Color="#FFF" />
    </Panel>

    <ClientPanel>
        <Panel Dock="Top" Height="46">
            <Text Value="NEWS" Color="#ECEFF1" FontSize="28" Alignment="Center" />
            <Rectangle Color="#37474F" />
            <Shadow />
        </Panel>
        <ScrollView>
            <StackPanel ItemSpacing="1">
                <Each Items="{data}">
                    <NewsItem />
                </Each>
            </StackPanel>
        </ScrollView>
    </ClientPanel>
</App>

Oh, I understand… As explained by Karsten Nikolai in the link you provided:

«Images converted from Base-64 gets saved to a temporary location. The filenames are generated from timestamps with second resolution, which means that images overwrites each other if more than one is loaded within one second»

maybe adding to the generated name a 6 random characters string could be sufficient. I really hope this will be fixed.

Thank you for helping.

To those who have ran into the same problem, the following code solves the issue (as suggested by Karsten Nikolai post mentioned by Anders above):

Add two requires:

var Base64 = require("FuseJS/Base64");
var FileSystem = require("FuseJS/FileSystem");

Remove all the ImageTools.getImageFromBase64({ }); part and replace it with the following code which decodes the base64 string to an ArrayBuffer and then it writes the returned buffer to a file:

var rndName = randomString(6) + ".jpg";

var buffer = new ArrayBuffer(4);
var buffer = Base64.decodeBuffer(item.imageencoded);
var fullImagePath = FileSystem.cacheDirectory + "/" + rndName;

FileSystem.writeBufferToFile(fullImagePath, buffer)
     .then(function() {
          item.image = fullImagePath;
              datas.add(item);
              //console.log("Successful write:" + fullImagePath);
          }, function(error) {
              //console.log(error);
});

Use this function to create a random filename:

function randomString(length) {
    var result = '';
    var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
    return result;
}

This solution has also the advantage that your base64 encoded images (which are now converted to files) are correctly displayed in the emulator (local preview) which, at the time of writing, does not work when using ImageTools.getImageFromBase64.

I’m using deliberately .cacheDirectory because files in this directory are automatically removed when space is low. You can use .dataDirectory but remember that files in this directory are persistent so you need to remove them periodically.