Blog

How to Split Images into Multiple Resolutions Via API Service

by on June 8, 2018

Images displayed in your app may be responsible for a large portion of the bandwidth consumed by the device. This has a direct impact on the app’s performance, battery consumption, and the amount of memory the app allocates. As a result, optimizing images can often bring noticeable performance improvements for your app: the fewer bytes it needs to download, the smaller the impact is on the client’s bandwidth and the faster app will download and render content on the screen.

Let’s imagine you have an app where you store pictures to show them to your app’s users. What happens if the resolution of these images is high and they are taking a lot of space? Downloading these files is time-consuming and, as a result, slows down your app. The result is a subpar user experience.

One recommended solution is to create image thumbnails with lower resolutions relative to the original one. These thumbnails can be used to preview the image in the application.

The thumbnails can be generated using Backendless API Services (in the Business Logic (Cloud Code) section). If you are not familiar with how to create your own API Service, please check the How to generate a QR code with Backendless API Service post, which describes the process of API service creation in greater detail.

In this article, we will focus on the task of generating thumbnail images with different resolutions.
Below is the source code for the service that performs the task:

import com.backendless.Backendless;
import com.backendless.servercode.BackendlessService;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
@BackendlessService
public class ImageResizer
{
 private static final double coefficient_1 = 0.75;
 private static final double coefficient_2 = 0.5;
 private static final double coefficient_3 = 0.25;
 private static final String repoStartPath = "/files/";
 public void resizeImage( String imageId ) throws IOException
 {
   // retrieving record from table 'images' from Backendless Data
   Map<String, Object> imageData = Backendless.Data.of( "images" ).findById( imageId );
   String stringUrl = (String) imageData.get( "original" );
   // parse url to retrieve file name and path (for saving in Backendless Files)
   URL imageUrl = new URL( stringUrl );
   String fullPath = imageUrl.getPath();
   String srcFileName = fullPath.substring( fullPath.lastIndexOf( '/' ) + 1 );
   String path = fullPath.substring( fullPath.indexOf( repoStartPath ) + repoStartPath.length(), fullPath.lastIndexOf( srcFileName ) - 1 );
   final String baseFileName = srcFileName.substring( 0, srcFileName.lastIndexOf( '.' ) );
   // read image in memory
   BufferedImage srcImg = ImageIO.read( imageUrl );
   // resize images with different coefficients
   BufferedImage img1 = getScaledImage( srcImg, coefficient_1 );
   String outputFileName = baseFileName + "__1.jpeg";
   saveImage( img1, path, outputFileName );
   BufferedImage img2 = getScaledImage( srcImg, coefficient_2 );
   outputFileName = baseFileName + "__2.jpeg";
   saveImage( img2, path, outputFileName );
   BufferedImage img3 = getScaledImage( srcImg, coefficient_3 );
   outputFileName = baseFileName + "__3.jpeg";
   saveImage( img3, path, outputFileName );
 }
 private BufferedImage getScaledImage( BufferedImage srcImg, double coefficient )
 {
   int targetWidth = (int) Math.round( srcImg.getWidth() * coefficient );
   int targetHeight = (int) Math.round( srcImg.getHeight() * coefficient );
   BufferedImage resizedImg = new BufferedImage( targetWidth, targetHeight, Transparency.OPAQUE );
   Graphics2D g2 = resizedImg.createGraphics();
   g2.setRenderingHint( RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC );
   g2.drawImage( srcImg, 0, 0, targetWidth, targetHeight, null );
   g2.dispose();
   return resizedImg;
 }
 private void saveImage( BufferedImage bufImg, String path, String fileName ) throws IOException
 {
   ImageWriter imageWriter = ImageIO.getImageWritersByFormatName( "jpeg" ).next();
   ImageWriteParam param = imageWriter.getDefaultWriteParam();
   param.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
   param.setCompressionQuality( 0.9F ); // set quality
   ByteArrayOutputStream baos = new ByteArrayOutputStream();
   imageWriter.setOutput( ImageIO.createImageOutputStream( baos ) );
   imageWriter.write( null, new IIOImage( bufImg, null, null ), param );
   Backendless.Files.saveFile( path, fileName, baos.toByteArray() );
   baos.close();
 }
}

We would also need at least one image file for testing. So, let’s go to the Files section and upload a test picture into the images directory:pasted image 0

Next, we’ll go to the Data section and create the images table with two fields:

column name: name, data type STRING

column name: original, data type FILE REFERENCE

Switch to the DATA BROWSER section and create a new object as shown below. The original column must contain a file reference to the file for which the thumbnails will be generated by the service:pasted image 0 (1)

Now run CodeRunner in debug mode and try your service in action using the API Services section of the Business Logic screen:

The imageId argument is an objectId of the record in the images table.

pasted image 0 (2)

And here is the result:

pasted image 0 (4)

As you can see, the newly created images have different file sizes. You can use any of those thumbnail types for the preview purposes in your app.

Leave a Reply