Guide Area
Guidearea banner

Send multiple images at once from PHP to Android

At some point during the Picsel development I had to start thinking about bulk actions, such as sending multiple pictures at once from my REST PHP webservice to Android application. I’m pretty ashamed to explain the way I did it before, but in order to describe the whole problem to you, I will state it anyway.

My application showed rankings of 10 best people. There were two tabs with these rankings that were initialized at the same time. That means that I needed to download 20 user-photo thumbnails at once. I developed this application in Denmark and my server is located in the middle of Europe, in Bratislava. The response time of the web service was between 20-30 ms, so when I made 20 unrelated web service calls, it made it less than 0.5 seconds to show the results. The problem is that for people from eastern side of Asia, it takes 1.0 – 1.2 seconds to finish one request (together about 20 seconds).

This is absolutely wrong! I made this mistake as this was the first time I worked with such a design, but I’m gonna show you a very simple workaround that you should implement if you’re facing a similar issue. I haven’t found any on the internet when I came across this problem so I hope it will be helpful.

So Picsel collected these 20 users and made request for each user. This request called my method downloadPhotoThumbnail($id, $filename) which then returned the photo. Both the parameters were equally important, because one user could have had multiple photos in his directory (due to reasons that I won’t describe here). All the pictures are converted to base64 string for transfer over HTTP like this:

function downloadPhotoThumbnail($id,$filename) {
    $imagedata = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/id_and_filename_parameters.jpg');
    return $imagedata;
}

The right side of the path was replaced using the parameters $id and $filename (replaced for security reasons, but you get the point). Now what we want to do is to download 20 of similar pictures, combine them into one big string and at the same time we still want to be able to divide them into 20 pictures back in Android. The simplest solution – custom separator. Imagine a string like this:

121!(*$!!r(!HR(!U(R!BBasdandiwa912893CUSTOMIMAGESEPARATORwadajkwndawod128u41*($@!J!@OIawjjnkdaawCUSTOMIMAGESEPARATORwdaajhdwa.......

You can notice that there are two CUSTOMIMAGESEPARATORs that are dividing the pictures from each other. The chance of this ‘CUSTOMIMAGESEPARATOR’ appearing in image string itself is probably around zero, although always make sure that it is a complex string which is unlikely to appear in the image string. Avoid separators ‘abc’, ‘123’ and so one.

There is not a possibility to send arrays in URL parameters (POST,GET,…). Although you can send a parameter like this:
http://example.com/downloadphotos.php?ids=1,2,4,7,9&filenames=first,second,fourth,seventh,ninth

You have to specify some separator here as well (I used a comma) because the whole parameter value of ids is nothing but a string “1,2,4,7,9”. After you receive this value in PHP web service, you have to again divide it into specific identifiers. So let’s put it all together in one method and we will get something like this:

function downloadRankingPhotos($ids,$filenames) {
    $array_id = explode(",",$ids);
    $array_filename = explode(",",$filenames);
    $result = "";
    for($i = 0; $i < sizeof($array_id); $i++) {
        $id = (int) $array_id[$i];
        $filename = $array_filename[$i];
        if ($i == sizeof($array_id) - 1) {
            // The last photo in array, no separator needed at the end
            $result .= downloadPhotoThumbnail($id,$filename);
        } else {
            // A photo and a custom separator
            $result .= downloadPhotoThumbnail($id,$filename) . "CUSTOMIMAGESEPARATOR";
        }
    }
    return $result;

Voila! You have a bulk action made of two methods. Now you can join as many photos as you like and send them to your Android (as long as they do not exceed the maximum PHP echo limit). As you now have one long string, you need to split it back to individual pictures. When you decode your web service response using Base64.decode(), you will get a byte array. I guess you already got an idea of creating a new String from this byte array and splitting it based on chosen custom separator. The difference is that because this is a cross-platform action, the PHP string can actually differ from the String recreated in Android. In my experience, the String that I received was not process-able by BitmapFactory (the problem might be in different charsets, try investigating in your own time if you’d like). The solution to this is to split the byte[] as returned by Base64. By doing this, you avoid conversion problems and you will get a series of byte arrays ready to be compiled into images. This method will help you do so:

public List<byte[]> splitBytes(byte[] array, byte[] delimiter) {
        List<byte[]> byteArrays = new LinkedList<>();
        if (delimiter.length == 0) {
            return byteArrays;
        }
        int begin = 0;

        outer:
        for (int i = 0; i < array.length - delimiter.length + 1; i++) {
            for (int j = 0; j < delimiter.length; j++) {
                if (array[i + j] != delimiter[j]) {
                    continue outer;
                }
            }
            byteArrays.add(Arrays.copyOfRange(array, begin, i));
            begin = i + delimiter.length;
        }
        byteArrays.add(Arrays.copyOfRange(array, begin, array.length));
        return byteArrays;
    }

byte[] array is what you got from Base64-decoded web service reponse, delimiter is the ‘CUSTOMIMAGESEPARATOR’ converted to byte array. Now that we have a List of byte[] representations of images, we can make Bitmaps out of them.

List<byte[]> bytes = splitBytes(Base64.decode(r, Base64.DEFAULT),separator.getBytes());
for (byte[] b : bytes) {
   Bitmap newbitmap = BitmapFactory.decodeByteArray(b, 0, b.length);

   ... whatever you wanna do with it

}

And that is all! It might be a bit complicated to understand at once, but after you do, you’ll realize that it is a very simple solution.

PS: Think a bit more about the bulk method from PHP. It is not very good to return the images without knowing which one of them belongs to what user. Even though they are ordered, it is not a good practice. You could for example come up with your own return structure, f.e. this would not be such a bad idea:

{
    [ "pictures" : {
        [ "id" : 1,
          "filename" : "first",
          "imagestring" : "adws!@*!2812nawrnkjra......." ],

        [ "id" : 2,
          "filename" : "second",
          "imagestring" : "sanfdnk!)(!sAS?"}}"}112......." ],

               ... some more objects ...
                   }
    ]
}

Happy coding!

Vladimir Marton

DevOps Engineer focused on cloud infrastructure, automation, CI/CD and programming in Javascript, Python, PHP and SQL. Guidearea is my oldest project where I write articles about programming, marketing, SEO and others.