Serialized Image content with Back & Forth Conversion

Joe

Thành viên VIP
21/1/13
3,074
1,344
113
(Continue of HERE)

Serialized Image content with Back & Forth Conversion

The downloaded Boutique on the Client site is slightly different from the Boutique main application. The appearance is however similar to the Direct-Sales of the provider package. But the Client app configuration file won't have the ImagePath and InvoicePath as well as the HumanResouce OODB name. However it constitutes of:
  • MessageListener and MessageSender (PubSub)
  • ListTableModel (customized AbtractTableModel)
  • ModelWorker (PubSub selector)
  • SalesTab
  • OpenOODB (Access to the OODB)
  • Utilities
  • Show Dialog (Imaging)
  • Parms POJO
  • Items, Invoice and HumanResource are the Serialized POJO OODB data
Note: the full sources (and all related JAR files) will be zipped and posted at the end of the last session of this series.

For an online sales bright Images are essential. Providers have two choices: images from the producers which are accessible on the web (via the links) or their own photographed images of the products which are stored in their host computer. And if the providers put these images on the web the accesses to the images could take some more time to appear than those that are accessed from the local hard drive. The easiest way is the link-way and is also the most chosen way by the most providers. They ignore and don't care much about the congestion on the web or the long waiting time for the consumers. Probably their "application" developers just use the tools chosen by their company (e.g. Lavarel, ReactJS, etc.) and are unimaginative on the self-developed alternative.

Every experienced developer knows that a browser must interpret each HTML page line by line and that is undeniable slow. If you don't believe me you should try to read and analyze some HTML pages of this site or any site you prefer and you could see how enormous and extensive the main HTML page is.
HTML:
<!DOCTYPE html>
<script data-ad-client="ca-pub-1277993641538901" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-33175243-2"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-33175243-2');
</script>

<html id="XF" lang="vi-VN" dir="LTR"
      style="font-size: 62.5%;"
      data-app="public"
      data-template="forum_list"
      data-container-key=""
      data-content-key=""
      data-logged-in="false"
      data-cookie-prefix="xf_"
      class="has-no-js template-forum_list  uix_page--fixed uix_hasWelcomeSection uix_hasSectionLinks  uix_hasPageAction"
      >
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">

    <title>Java Việt Nam, Java Căn bản, Spring framework, J2EE | Cộng đồng Java Việt Nam | Java Việt Nam | Java SE, Java EE, Android, Spring Framework</title>

        <meta name="description" content="Cộng đồng Java Việt Nam | Java Việt Nam | Java SE, Java EE, Android, Spring Framework | Diễn đàn trao đổi Java Việt Nam" />
        <meta property="og:description" content="Cộng đồng Java Việt Nam | Java Việt Nam | Java SE, Java EE, Android, Spring Framework | Diễn đàn trao đổi Java Việt Nam" />
        <meta property="twitter:description" content="Cộng đồng Java Việt Nam | Java Việt Nam | Java SE, Java EE, Android, Spring Framework | Diễn đàn trao đổi Java Việt Nam" />
            <link rel="canonical" href="https://congdongjava.com/forum/" />

            <link rel="alternate" type="application/rss+xml" title="RSS feed for Cộng đồng Java Việt Nam | Java Việt Nam | Java SE, Java EE, Android, Spring Framework" href="/forum/forums/-/index.rss" />

    <meta property="og:site_name" content="Cộng đồng Java Việt Nam | Java Việt Nam | Java SE, Java EE, Android, Spring Framework" />
    <meta property="og:type" content="website" />
        <meta property="og:title" content="Java Việt Nam, Java Căn bản, Spring framework, J2EE" />
        <meta property="twitter:title" content="Java Việt Nam, Java Căn bản, Spring framework, J2EE" />
    <meta property="og:url" content="https://congdongjava.com/forum/" />
...
A long page, isn't it? And the Upload of images is the greatest bottleneck. Some mediocre application developers simply take the images as they are: without any optimization, nor any reduction of the sheer big size. Some cameras produce the best pictures because the images are dotted by several mega pixels. With some simple tools (e.g. Paint of Microsoft) fat images can be slimmed down radically without hurting the quality of the images. Well, because our eyes are not the hawk's eyes :D. Btw, beside Paint of Microsoft you can download my JavaFX tool from THIS FORUM: JavaFX Imaging.

So, fat image slim-down is good, but how can an image be serialized and stored together with the other objects? The problem is the compressed content of an image. Pre Java 7 image has to be circumvented with complex codes if it should be converted to a byte[] (as you could find on the Web). Example:
Java:
  BufferedImage bImage = ImageIO.read(new File("sample.jpg"));
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  ImageIO.write(bImage, "jpg", bos );
  byte [] data = bos.toByteArray();
(Source: HERE).

The post JAVA 7 the conversion of an image to a byte[] is much simpler and easier than the above coding.
Java:
  byte[] bb = Files.readAllBytes((new File("sample.jpg")).toPath());
With the byte[] the content of an image can be now serialized together with other objects. But a byte[] is absolutely inappropriate for a presentation in a JTable. How about a String? Well, much better. However, it isn't as easy as some application developers think. Conversion of a byte[] with binary content to a String is a real know-how hard work. The following example underscores the problem of byte[]-String conversion:
Java:
// Joe Nartca (C)
public class ImageString {
  public static void main(String[] args) throws Exception {
    String pic = args.length > 0? args[0]:"boss.jpg";
    JFrame frame = new JFrame("Image & String");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // conventional way but won't work....correctly
    String img1 = image_String(pic);
    JButton but1 = new JButton(new ImageIcon(to_Image(img1)));

    // Joe's way and it works
    String img2 = imageString(pic);
    JButton but2 = new JButton(new ImageIcon(toImage(img2)));

    JPanel panel = new JPanel();
    panel.add(but1);
    panel.add(but2);
    frame.add("Center", panel);
    frame.pack();
    frame.setVisible(true);
  }
  // conventional way
  public static String image_String(String image) {
    try {
      byte[] bb = Files.readAllBytes((new File(image)).toPath());
      return new String(bb);
    } catch (Exception ex) { }
    return image;
  }
  public static BufferedImage to_Image(String image) {
    try {
      return ImageIO.read(new ByteArrayInputStream(image.getBytes()));
    } catch (Exception ex) { }
    return null;
  }
  // Joe's way
  public static String imageString(String image) {
    try {
      byte[] bb = Files.readAllBytes((new File(image)).toPath());
      StringBuilder sb = new StringBuilder(bb.length);
      for (byte b : bb) sb.append((char)(b & 0xFF));
      return sb.toString();
    } catch (Exception ex) { }
    return image;
  }
  // Convert ImageString back to BufferedImage
  public static BufferedImage toImage(String image) {
    try {
      ByteArrayOutputStream bao = new ByteArrayOutputStream();
      for (int i = 0, len = image.length(); i < len; ++i) {
        bao.write(new byte[] { (byte)image.charAt(i) } );
      }
      return ImageIO.read(new ByteArrayInputStream(bao.toByteArray()));
    } catch (Exception ex) { }
    return null;
  }
}
Explanation:
  • The conventional way is also the erroneous way. The static methods image_String() converts a byte[] to a string using the "known" String constructor: new String(byte[]). The other method to_Image() reverses the image_String() to a byte[] using the known String method getBytes(). Note that the last method work only with JPG image and won't work with, for example, PNG image (IIOexception).
  • The JoeNartca way exploits the known fact that Java primitives are always signed as positive or negative. Casting is the only way to neutralize the sign. Because a byte has a range between -128...127 a byte with the value 0x80 is -128 and it gets lost on conversion to a part of a String. It becomes garbage on the way of back-conversion to a byte. But the fact is: a primitive char (2 bytes) can have the 0x80 value. Hence:
Java:
    byte b = (byte)0x80;
    //
    char c = (char)(b & 0xFF); //<--neutralize the sign
    byte x = (byte)c;
    System.out.printf("1: %02X %04X %02X\n", b,(int)c,x);
    //
    // let the sign propagates...as a sign
    c = (char) b;
    System.out.printf("2: %02X %04X %02X\n", b,(int)c,x);
The output shows you where the conversion problem lies:
Code:
C:\links\java\CDJ>java ImageString
1: 80 0080 80
2: 80 FF80 80
FF80 is the negative value -128, not the real value of an unsigned byte 0x80. And if you compile and run this ImageString you get this screen:

imageString.png

The original: Boss.jpg

Boss.jpg

Conclusion: to serialize an image content from a byte[] it is essential to neutralize the signed bytes to unsigned bytes. And the transfer of a serialized object with an ImageString to the clients where it is reconverted to an image is more effective than an Upload of the original via HTML to the clients.

The following snippets shows you how a big, fat image can be slimmed down:
Java:
          BufferedImage img = Utilities.toImage((String)obj);
          double ratio = 1d;
          int w = img.getWidth();
          int h = img.getHeight();
          // resizing the image ?
          if (h > 900)  ratio = 0.40d;
          else if (h > 800)  ratio = 0.45d;
          else if (h > 600)  ratio = 0.55d;
          else if (h > 500)  ratio = 0.6d;
          if (ratio == 1d) {
            if (w > 800) ratio = 0.6d;
            else if (w > 700) ratio = 0.7d;
          }
          // must be reduced?
          if (ratio < 1d) {
            w = (int)Math.ceil(w * ratio);
            h = (int)Math.ceil(h * ratio);
            Image image = img.getScaledInstance(w, h, Image.SCALE_SMOOTH);
            img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
            img.getGraphics().drawImage(image, 0, 0 , null);
          }
(continue HERE)
 
Sửa lần cuối: