Bootstrap

JAVA对于图片的花式操作(不定期更新)

MultipartFile 转 Base64字符串

/**
 * MultipartFile文件转bash64字符串
 *
 * @param multiPartFile  文件流
 * @param isPrefix       是否需要前缀
 * @return
 */
public static String multiPartFileToBase64String(
											MultipartFile multiPartFile, 
											boolean isPrefix) {
    String baseStr = null;
    BASE64Encoder encoder = new BASE64Encoder();
    try {
        baseStr = encoder.encode(multiPartFile.getBytes());
        baseStr = baseStr.replaceAll("\r\n", "");
        if (isPrefix) {
            baseStr = "data:image/jpeg;base64," + baseStr;
        }
    } catch (IOException e) {
    }
    return baseStr;
}

图片url 转 Base64字符串

/**
 * 图片URL转Base64编码
 *
 * @param imagePath 图片URL
 * @param isPrefix  是否需要前缀
 * @return Base64编码
 */
public static String imageUrlToBase64(String imagePath, boolean isPrefix) {
    ByteArrayOutputStream outPut = new ByteArrayOutputStream();
    byte[] data = new byte[1024];
    InputStream inStream = null;
    try {
        // 创建URL
        URL url = new URL(imagePath);
        // 创建链接
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(10 * 1000);
        if (conn.getResponseCode() != 200) {
            //连接失败/链接失效/图片不存在
            return "fail";
        }
        inStream = conn.getInputStream();
        int len = -1;
        while ((len = inStream.read(data)) != -1) {
            outPut.write(data, 0, len);
        }
        // 对字节数组Base64编码
        byte[] encode = Base64.getEncoder().encode(outPut.toByteArray());
        if (isPrefix) {
            String res = String.format("%s%s", "data:image/jpeg;base64,", new String(encode));
            return res;
        }
        return new String(encode);

    } catch (Exception e) {
    } finally {
        try {
            inStream.close();
        } catch (Exception e) {
        }
    }
}

图片url 转 MultipartFile

/**
 * 在线图片url转成MultipartFile对象
 *
 * @param url  图片url
 * @return
 */
public static MultipartFile imgUrlToMultiPartFile(String url) {
    URLConnection connection = null;
    InputStream inputStream = null;
    byte[] bytes = null;
    try {
        connection = new URL(url).openConnection();
        inputStream = connection.getInputStream();
        bytes = StreamUtils.copyToByteArray(inputStream);
    } catch (Exception e) {
        log.error(e.toString());
    }
    String contentType = connection.getContentType();
    String[] contentTypes = contentType.split("/");
    String filename = "file." + contentTypes[1];
    return new ByteArrayMultipartFile(bytes, filename, contentType);
}

图片url 转 File流

/**
 * url资源转化为file流
 *
 * @param url   图片url
 * @return
 */
public static File urlToFile(URL url) {
    InputStream is = null;
    File file = null;
    FileOutputStream fos = null;
    try {
        file = File.createTempFile("tmp", null);
        URLConnection urlConn = null;
        urlConn = url.openConnection();
        is = urlConn.getInputStream();
        fos = new FileOutputStream(file);
        byte[] buffer = new byte[4096];
        int length;
        while ((length = is.read(buffer)) > 0) {
            fos.write(buffer, 0, length);
        }
        return file;
    } catch (Exception e) {
    } finally {
        if (is != null) {
            try {
                is.close();
            } catch (Exception e) {
            }
        }
        if (fos != null) {
            try {
                fos.close();
            } catch (Exception e) {
            }
        }
    }
}

base64图片 压缩

引入依赖

<dependency>
	<groupId>net.coobird</groupId>
	<artifactId>thumbnailator</artifactId>
	<version>0.4.20</version>
</dependency>

代码编写

    /**
     * 目标大小不可设置太过准确,也不可太小,例如:需要压缩至150k以下的图片,那就传入140或者100左右的一个值即可
     *
     * @param imgBase64      原图片base64,含不含前缀【data:image/jpeg;base64,】都可
     * @param targetSize     压缩图片目标大小,默认100KB
     * @param throwException 出错时是否报错
     * @return 返回的base64内容不含【data:image/jpeg;base64,】前缀
     */
    public static String compressPic(String imgBase64, int targetSize, boolean throwException){
        if(StringUtils.isEmpty(imgBase64)){
            log.error("imgBase64 param not null ......");
            if(throwException){
                throw new MixException(BusinessErrorEnum.BASE_PARAM_FORMAT);
            }
        }
        targetSize = targetSize <= 0 ? 100 : targetSize;
        String newBase64 = imgBase64;
        InputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        try {
            if(imgBase64.startsWith("data:")){
                imgBase64 = imgBase64.split(",")[1];
            }
            // 解码 Base64 图片数据
            byte[] imageBytes = Base64.getDecoder().decode(imgBase64);
            // 计算图片大小(KB)
            float imageOriginalSize = (float) imageBytes.length / 1024;
            log.info("--------------compressedImageBytes#imageOriginalSize(Byte) = {}", imageOriginalSize);
            //图片大小已经比目标值小,直接返回原图base64
            if(imageOriginalSize < targetSize){
                return newBase64;
            }
            // 创建输出流,用于存储压缩后的图片数据
            outputStream = new ByteArrayOutputStream();
            int index = 0;
            byte[] compressedImageBytes = imageBytes;
            // 图片大小压缩比例
            BigDecimal scale = new BigDecimal(1.0f);
            float quality = targetSize / imageOriginalSize;
            // 先进行质量压缩,如果质量压缩大小最终高于目标值,那就进行图片大小压缩
            while (scale.floatValue() >= 0.1 && compressedImageBytes.length / 1024 > targetSize){
                // 创建输入流,读取解码后的图片数据
                inputStream = new ByteArrayInputStream(imageBytes);
                outputStream.reset();
                Thumbnails.of(inputStream)
                        .scale(scale.floatValue())
                        .outputFormat("jpg")
                        .outputQuality(quality)
                        .toOutputStream(outputStream);
                // 获取压缩后的图片数据
                compressedImageBytes = outputStream.toByteArray();
                scale = scale.subtract(new BigDecimal(0.05f));
                ++index;
                log.info("--------------compressedImageBytes#index = {},scale = {}, nowSize = {}", index, scale.floatValue(), compressedImageBytes.length / 1024);
            }

            byte[] encode = Base64.getEncoder().encode(compressedImageBytes);
            newBase64 = new String(encode);
            log.info("--------------compressedImageBytes#nowImgSize(Byte) = {}", compressedImageBytes.length / 1024);
        }catch (Exception e){
            log.error("compressPic business error:", e);
            if(throwException){
                throw new MixException(BusinessErrorEnum.BASE_SERVER_UNKNOWN);
            }
        }finally {
            // 关闭流
            try {
                if(inputStream != null){
                    inputStream.close();
                }
                if(outputStream != null){
                    outputStream.close();
                }
            } catch (Exception e) {
                log.error("compressPic close stream error:", e);
                if(throwException){
                    throw new MixException(BusinessErrorEnum.BASE_SERVER_UNKNOWN);
                }
            }

        }
        return newBase64;
    }

抠图

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RasterFormatException;
import java.io.*;

@Slf4j
public class ImageUtil {

    private final static String PNG = "png";

    private final static String JPG = "jpg";

    private final static String JPEG = "jpeg";

    private final static long TWO_MB = 1024 * 1024 * 2;
    /**
     * 切割图片,以左上角坐标为起点,切割指定宽高的图片
     * @param file
     * @param faceDetectVo
     * @return
     */
    public static MultipartFile slicingImage(InputStream file, String fileType, FaceDetectVo faceDetectVo){
        // 读取原始图片
        Integer width = faceDetectVo.getW();
        Integer high = faceDetectVo.getH();
        Integer x = faceDetectVo.getX();
        Integer y = faceDetectVo.getY();
        try {
            BufferedImage originalImage = ImageIO.read(file);
            int maxWidth = originalImage.getWidth(); // 获取原图的宽度
            int maxHigh= originalImage.getHeight(); // 获取原图的高度
            Color backgroundColor = getDominantColor(originalImage); // 获取原图的背景色
            // 校验参数
            x = x > 0 ? x : 0;
            y = y > 0 ? y : 0;
            width = width > 0 ? width : 0;
            high = high > 0 ? high : 0;
            width = x + width > maxWidth ? maxWidth - x: width;
            high = y + high > maxHigh ? maxHigh - y : high;
            BufferedImage subImage = originalImage.getSubimage(x, y, width, high);
            // 处理png格式的
            if (PNG.equals(fileType)){
                // 创建带背景颜色的新图像
                BufferedImage imageWithBackground = new BufferedImage(width, high, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = imageWithBackground.createGraphics();
                g2d.setColor(backgroundColor);
                g2d.fillRect(0,0, width, high);
                g2d.drawImage(subImage, 0, 0, null);
                g2d.dispose();
                MultipartFile multipartFile = convertBufferedImageToMultipartFile(subImage, "data.png", PNG);
                // 如果把jpg的图片后缀手动改为png,读取文件流的时候会文件变大,这种情况给他改为jpg
                if (multipartFile.getSize() > TWO_MB){
                    multipartFile = convertBufferedImageToMultipartFile(subImage, "data.jpg", JPG);
                    log.info("抠图大小大于2mb, 尝试转为jpg, 文件大小为: {}", multipartFile.getSize());
                }
                System.out.println("图片切分完成!");
                return multipartFile;
            }
            // 处理jpg, jpeg格式的
            // 创建带背景颜色的新图像
            BufferedImage imageWithBackground = new BufferedImage(width, high, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = imageWithBackground.createGraphics();
            g2d.setColor(backgroundColor);
            g2d.fillRect(0,0, width, high);
            g2d.drawImage(subImage, 0, 0, null);
            g2d.dispose();
            MultipartFile multipartFile = convertBufferedImageToMultipartFile(imageWithBackground, "data." + fileType, fileType);
            System.out.println("图片切分完成!");
            return multipartFile;
        } catch (IOException e) {
            log.error("图片切分失败!", e);
        } catch (RasterFormatException e) {
            log.info("切分坐标超出图片范围,请检查坐标设置。");
        }
        return null;
    }


    // 获取原图的背景色(可以改进为更复杂的算法)
    private static Color getDominantColor(BufferedImage image) {
        // 这里简单返回左上角像素的颜色作为背景色
        return new Color(image.getRGB(0, 0));
    }

    private static MultipartFile convertBufferedImageToMultipartFile(BufferedImage image, String fileName, String fileType) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, fileType, baos);
        InputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
        return new MultipartFile() {
            @Override
            public String getName() {
                return fileName;
            }
            @Override
            public String getOriginalFilename() {
                return fileName;
            }
            @Override
            public String getContentType() {
                return "image/" + fileType;
            }
            @Override
            public boolean isEmpty() {
                return baos.size() == 0;
            }
            @Override
            public long getSize() {
                return baos.size();
            }
            @Override
            public byte[] getBytes() throws IOException {
                return baos.toByteArray();
            }
            @Override
            public InputStream getInputStream() throws IOException {
                return inputStream;
            }
            @Override
            public void transferTo(File dest) throws IOException, IllegalStateException {
                // 这里可以实现文件转移逻辑
            }
        };
    }

    public static String getFileType(MultipartFile file) {
        String fileName = file.getOriginalFilename().toLowerCase();

        if (fileName.endsWith(StringPool.DOT + PNG)) {
            return PNG;
        }
        if (fileName.endsWith(StringPool.DOT + JPG)) {
            return JPG;
        }
        if (fileName.endsWith(StringPool.DOT + JPEG)) {
            return JPEG;
        }else {
            return PNG;
        }

    }
}
;