[Java Toolbox] Picture-Base64 Interchange

Keywords: Java encoding Javascript Apache

Preface

Recently I was really annoyed by the ability to upload pictures.In web projects, we often have business scenarios for uploading pictures, the most typical of which is uploading avatars.In order to solve the problem on the avatar, the following can be implemented:

  1. Use multipart/form-data to upload user information and avatars, that is, use <form></form> in html.Such as a gitlab avatar that modifies user information.
  2. First upload the picture to the picture service and get the picture connection, then use the picture connection to modify the user information.
  3. Upload the Base64 encoding information of the picture directly, as the data of the picture, and then convert the encoding into a picture file in the background.

Here we will discuss how the pictures in the third implementation rotate with Base64 encoding.

There are two ways to process pictures on a Web page: directly src="/avatar/avatar.jpg", and src="data:image/jpeg;base64,xxxxxx=".The second way is that the front end will send content to the background. The data consists of [Data Description], [Data Base64], [Data Description] will tell us the category of the picture, from which the extension of the picture can be analyzed, [Data Base64] will be the data encoded for the picture Base64, which will be the complete data for the picture file.In order to fully reply to the content and extension of the picture, the front end needs to send [Data Description], [Data Base64] to the background.The format is actually a Data URI Scheme, which is explained later.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <p>Data URLs Image:</p>
    <!-- Common ways -->
    <img src="/avatar/avatar.jpg">
    <!-- take img.src Copy and paste the contents of the picture into the browser's input box -->
    <img src="data:image/jpeg;base64,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" />
</body>
</html>

Judging picture extensions from [Data Description]

Specific implementation

Mapping of data descriptions and extensions

Here, two maps are used to record the mapping of data descriptions to extensions and extensions to data descriptions, respectively, to facilitate the acquisition of data descriptions and extensions.

import java.util.HashMap;
import java.util.Map;
import java.io.File;

public class ImageDataURISchemeMapper {
    private static Map<String, String> scheme2Extension = new HashMap<String, String>();
    private static Map<String, String> extension2Scheme = new HashMap<String, String>();
    
    static {
        initSchemeSupported();
    }

    public static String getScheme(String imageExtension) {
        if (imageExtension == null || imageExtension.isEmpty()) {
            return "";
        }
        String result = extension2Scheme.get(imageExtension.toLowerCase());
        return result == null ? "" : result;
    }

    public static String getScheme(File image) {
        if (image == null) {
            return "";
        }
        String name = image.getName();
        int lastPointIndex = name.lastIndexOf(".");
        return lastPointIndex < 0 ? "" : getScheme(name.substring(lastPointIndex + 1));
    }
    
    public static String getExtension(String dataUrlScheme) {
        return scheme2Extension.get(dataUrlScheme);
    }

    public static String getExtensionFromImageBase64(String imageBase64, String defaultExtension) {
        int firstComma = imageBase64.indexOf(",");
        if(firstComma < 0) {
            return defaultExtension;
        }
        return scheme2Extension.get(imageBase64.subSequence(0, firstComma + 1));
    }
    
    private static void initSchemeSupported() {
        addScheme("jpg", "data:image/jpg;base64,");
        addScheme("jpeg", "data:image/jpeg;base64,");
        addScheme("png", "data:image/png;base64,");
        addScheme("gif", "data:image/gif;base64,");
        addScheme("icon", "data:image/x-icon;base64,");
    }
    
    private static void addScheme(String extension, String dataUrl) {
        scheme2Extension.put(dataUrl, extension);
        extension2Scheme.put(extension, dataUrl);
    }
}

Picture to Base64 and Base64 to Picture

Picture to Base64:

  1. Reads the picture file as a data stream and converts it into a byte array
  2. Base64 encoding the byte array and converting it to a string
  3. Add data description prefix based on file extension

Base64 to Picture:

  1. Split the Base64 string into data description and data Base64 parts
  2. Get picture extensions from the Data Description section
  3. Decode the data Base64 to get a byte array
  4. Save the byte array to the file. If the path to the saved file provides the full file name, no extension is required, otherwise the extension is used as the picture file name extension.
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.IOUtils;

/**
 * The categories of pictures to be processed are: png, jpg, jpeg
 * 
 * Reference resources:
 * <ul>
 *   <li>[Brief analysis of data:image/png; application of base64] (https://www.cnblogs.com/ECJTUACM-873284962/p/9245474.html)</li>
 *   <li>[Base64](https://zh.wikipedia.org/wiki/Base64)</li>
 *   <li>[Data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs)
 * </ul>
 * 
 * @author DoneSpeak
 * @date 2019/06/26
 */
public class ImageConvertBase64 {

    /**
     * Convert picture files to byte arrays
     * 
     * @param image
     *            Picture file to process
     * @return byte array converted from picture file
     */
    public static byte[] toBytes(File image) {
        try (FileInputStream input = new FileInputStream(image)) {
            // The available() of InputStream returns the length of data that the InputStream can read at one time without blocking.
            // byte[] imageBytes = new byte[input.available()];
            // input.read(imageBytes);
            return IOUtils.toByteArray(input);
        } catch (IOException e) {
            return null;
        }
    }

    public static String toBase64(byte[] bytes) {
        return bytesEncode2Base64(bytes);
    }
    
    /**
     * Convert Picture to String of base64
     * 
     * @param image
     *            Picture file to process
     * @return base64 string converted from picture file
     */
    public static String toBase64(File image) {
        return toBase64(image, false);
    }
    
    /**
     * Convert the picture to a string of base64.If the value of <code>appendDataURLScheme</code>is true, expand the Data URL scheme for the base64 string of the picture.
     * @param image Path to Picture File
     * @param appendDataURLScheme Whether to expand the Data URL scheme prefix
     * @return base64 string converted from picture file
     */
    public static String toBase64(File image, boolean appendDataURLScheme) {
        String imageBase64 = bytesEncode2Base64(toBytes(image));
        if(appendDataURLScheme) {
            imageBase64 = ImageDataURISchemeMapper.getScheme(image) + imageBase64;
        }
        return imageBase64;
    }

    private static String bytesEncode2Base64(byte[] bytes) {
        return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
    }

    private static byte[] base64Decode2Bytes(String base64) {
        return Base64.getDecoder().decode(base64);
    }

    /**
     * Restore byte array to picture file
     * 
     * @param imageBytes
     *            byte array of picture files
     * @param imagePath
     *            Save Address for Recovered Picture Files
     * @return If the build is successful, the generated file path is returned, where the result is <code>imagePath</code> for the parameter.Otherwise return null
     */
    public static File toImage(byte[] imageBytes, File imagePath) {
        if (!imagePath.getParentFile().exists()) {
            imagePath.getParentFile().mkdirs();
        }
        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(imagePath))) {
            bos.write(imageBytes);
            return imagePath;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * Restore base64 string to picture file
     * 
     * @param imageBase64
     *            base64 string for picture file
     * @param imagePath
     *            Save Address for Recovered Picture Files
     * @return If the build is successful, the generated file path is returned, where the result is <code>imagePath</code> for the parameter.Otherwise return null
     */
    public static File toImage(String imageBase64, File imagePath) {
        // There is no','in the base64 string.
        int firstComma = imageBase64.indexOf(",");
        if(firstComma >= 0) {
            imageBase64 = imageBase64.substring(firstComma + 1);
        }
        return toImage(base64Decode2Bytes(imageBase64), imagePath);
    }

    /**
     * Save imageBase64 to the specified file.If <code>fileName</code>contains an extension, use the extension <code>fileName</code>directly.
     * Otherwise, if <code>imageBase64</code>is a Data URLs, the extension will be judged by a more prefix.If the extension cannot be determined, use "png" as the default extension.
     * @param imageBase64 base64 encoded string for picture
     * @param dir Directory to save pictures
     * @param fileName Name of picture
     * @return If the build is successful, the generated file path is returned.Otherwise return null
     */
    public static File toImage(String imageBase64, File dir, String fileName) {
        File imagePath = null;
        if(fileName.indexOf(".") < 0) {
            String extension = ImageDataURISchemeMapper.getExtensionFromImageBase64(imageBase64, "png");
            imagePath = new File(dir, fileName + "." + extension);
        } else {
            imagePath = new File(dir, fileName);
        }
        return toImage(imageBase64, imagePath);
    }
}

Use third-party tool classes to simplify code.

The tool class used here is org.apache.commons.io. *:

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

Get File Extensions

int lastPointIndex = filename.lastIndexOf(".");
String extension = lastPointIndex < 0 ? "" : getScheme(filename.substring(lastPointIndex + 1));
// Simplify
String extension = FilenameUtils.getExtension(filename);

File to byte[]

File image = new File("avatar.jpg");
IOUtils.toByteArray(new FileInputStream(image));
// perhaps
byte[] bytes = FileUtils.readFileToByteArray(image);

byte[] saved as a file

File image = new File("avatar.jpg")
FileUtils.writeByteArrayToFile(image, bytes);

The code to convert pictures to Base64 and Base64 to pictures is actually just a few lines of code.

public static void main(String[] args) throws IOException {
    File image = new File("src/test/resources/imageConvertBase64.jpeg");
    File newImage = new File("src/test/resources/new-imageConvertBase64.jpeg");

    // Encoded as Base64 String
    byte[] bytes = FileUtils.readFileToByteArray(image);
    String base64 = new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
    System.out.println(base64);
    // Base64 Save as Picture
    bytes = Base64.getDecoder().decode(base64);
    FileUtils.writeByteArrayToFile(newImage, bytes);
}

Expand knowledge

Data URLs

Data URLs, URLs prefixed with the data: scheme, allow content creators to embed small files inline in documents.

-- Data URLs @Mozilla

Data URLs are made up of four parts, such as data: image/jpeg at the beginning of the article; base64, /9j/4AAQSkxxxxxx.

data:[<mediatype>][;base64],<data>
  • data: Protocol fixed prefix.
  • [<mediatype>]: is a MIME type For example, image/jpeg, you can also "Incomplete list of MIME types" Some types were found in.
  • [; base64]: is the encoding method.Base64 used here.
  • <data>: Encoded string.

The Data URL schema is RFC2397 The URL scheme defined in the.It has the following advantages and disadvantages:

Advantage

  • Reduce Requests
  • When external resources are limited, they are available

shortcoming

  • Cannot Reuse
  • Can't cache independently (can be cached with CSS file using background-image of css)
  • base64 will increase the size of the original file by one-third

The types currently supported by the Data URL schema are:

type describe
data:, Text Data
data:text/plain, Text Data
data:text/html, HTML Code
data:text/html;base64, HTML code encoded by base64
data:text/css, CSS Code
data:text/css;base64, base64-encoded CSS code
data:text/javascript, Javascript code
data:text/javascript;base64, Javascript code encoded by base64
data:image/gif;base64, base64 encoded gif picture data
data:image/png;base64, base64 encoded png picture data
data:image/jpeg;base64, base64-encoded jpeg picture data
data:image/x-icon;base64, base64 encoded icon picture data

More detailed information can be found: Analysis of data:image/png; application of base64@Angel_Kitty

Base64

Base64 is a representation of binary data based on 64 printable characters.Any data is binary, meaning Base64 can represent any data.Base64 is often used to represent, transfer, and store some binary data, including MIME e-mail and some complex XML data, when text data is usually processed.Its main function is not to ensure security, but to make the data transfer in the network error-free.

Because 2^6=64, every six bits is a unit, corresponding to a printable character.Three bytes have 24 bits, corresponding to four Base64 units (24/6=4), that is, three bytes can be represented by four printable characters.This results in a 1/3 increase in the length of the encoded data over the original data.Each 6 bits is selected according to ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/If the original data length is not a multiple of 3, then there is one input character left, one=, two input characters and one= in the encoding result.

Encoding "Man"

Encoding in the last bit (A) or two characters (BC)

Decode

The reverse process of encoding, converting = to 0, is sufficient. ASCII Code, the 0 character is empty.

The above is basically from Wikipedia Base64 .Due to the particularity of +/, in order to adapt to different scenarios, the +/ in the original algorithm will be replaced with different characters to form a new algorithm.

In Java 8, JDK provides a codec tool for Base64.Provides Basic encoding URL encoding MIME encoding and encapsulation of streams, articles Java 8 Implements BASE64 Codec A good introduction to the Base64 tool is provided.

Base64 Conversion Tool:

Posted by coreyk67 on Sun, 01 Sep 2019 09:22:15 -0700