Graphics2D - 生成证书图片

需求:用户领取证书功能。起初做法是将证书内容保存至富文本编辑器,再将富文本内容返回,由前端组成证书页面。但是前端因为屏幕尺寸、拉伸等问题,导致展示的效果不尽人意,无法满足业务要求。所以选择了使用Graphics2D在后端生成证书图片,再将图片返回的方式。

一、使用类

Graphics2D

Graphics类,提供了对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制。用于在Java平台上呈现二维形状、文本和图像的基础类。Graphics2D对象相当于一块画布,我们可以使用Graohics2D提供的方法在画布上进行绘制。

BufferedImage

BufferedImage是带缓冲区图像类,主要作用是将图片加载到内存中,用于对图片进行一些绘图操作。

ByteArrayOutputStream

ByteArrayOutputStream是一个输出流,用于将数据输出到字节数组中,可以通过toByteArray()方法获取写日的字节数组,也可以通过toString()方法将字节数组转换为字符串。

二、生成证书图片

1.准备工作:

(1)模板底图(包含固定文字信息)
(2)填入的信息(比如:姓名、编号等),记录底图中需要填写信息位置的坐标轴(x,y)。

[{name:"Java123",x:"374",y:"298"},{name:"小李",x:"570",y:"299"},{name:"Graphics杯",x:"320",y:"351"}]

待写入具体信息的模板底图

2、工具类

(1)引入依赖


<dependency>
	<groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.14</version>
</dependency>

(2)绘图工具类

import cn.hutool.core.codec.Base64;
import cn.hutool.core.img.FontUtil;
import cn.hutool.core.img.GraphicsUtil;
import cn.hutool.core.img.Img;
import cn.hutool.core.img.ImgUtil;
import org.springframework.core.io.ClassPathResource;


import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;

public class GraphicsUtils {

    private static final String prefix = "/graphics.background/";

    private static Font baseFont;
    private static Font normalFont;
    private static Font ltBoldFont;

    static {
        //基础字体
        //每次Font.createFont都会生成一个临时文件,多了磁盘就爆了
        try {
        	// 宋体字资源
            ClassPathResource songFontResource = new ClassPathResource(prefix + "simsun.ttc");
            baseFont = Font.createFont(Font.TRUETYPE_FONT, songFontResource.getInputStream());
            normalFont = Font.createFont(Font.TRUETYPE_FONT, songFontResource.getInputStream());
           	// 黑体字资源
            ClassPathResource ltFontResource = new ClassPathResource(prefix + "Lantinghei.ttc");
            ltBoldFont = Font.createFont(Font.TRUETYPE_FONT, ltFontResource.getInputStream());
        } catch (FontFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 写字
     * @param graphics
     * @param text
     * @param font
     * @param x
     * @param y
     * @param align
     */
    public static void drawString(Graphics2D graphics, String text, Font font, int x, int y, int align)
    {
    	// 获得字体对应字符串的长宽信息
        Dimension dimension = FontUtil.getDimension(graphics.getFontMetrics(font), text);
        int newX = x;
        if(align == 0) {
            newX = (int) (x-dimension.getWidth()/2);
        }
        else if(align == -1){
            newX = x;
        }
        else{
            newX = (int) (x-dimension.getWidth());
        }
        GraphicsUtil.drawString(graphics,text, font,Color.BLACK,new Point(newX, y));
    }

    public static int drawStringWithMultiLine(Graphics2D graphics2D, String text,Font font, int maxWidth, int startX, int startY, int heightSpace,int indent)
    {
        return drawStringWithMultiLine(graphics2D, text, font, maxWidth, startX, startY, heightSpace, indent, true);
    }

    /**
     * 输出整行字符串,超过换行
     * @param graphics2D
     * @param text
     * @param font
     * @param maxWidth
     * @param startX
     * @param startY
     * @param heightSpace
     * @param indent
     * @return
     */
    public static int drawStringWithMultiLine(Graphics2D graphics2D, String text,Font font, int maxWidth, int startX, int startY, int heightSpace,int indent,boolean draw)
    {
        int lastY = startY;
        // 获取指定字体的字体度量。
        FontMetrics fontMetrics = graphics2D.getFontMetrics(font);
        Dimension dimension = FontUtil.getDimension(fontMetrics, text);
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<indent;i++)
        {
            //全角空格
            sb.append('\u3000');
        }
        sb.append(text);

        text = sb.toString();

        int textLength = text.length();
        int totalWidth = (int) dimension.getWidth(); //一行的总长度,用于判断是否超出了范围

        int textHeight = (int) dimension.getHeight(); //计算一行的高度

        int rightOffset = maxWidth - startX;
        if (totalWidth > rightOffset) {
            //总长度超过了整个长度限制
            int alreadyWriteLine = 0; //已经写了多少行
            int nowWidth = 0; //目前一行写的长度
            for (int i = 0; i < textLength; i++) {
                int oneWordWidth = fontMetrics.charWidth(text.charAt(i)); //获取单个字符的长度
                int tempWidth = oneWordWidth + nowWidth; //判断目前的一行加上这个字符的长度是否超出了总长度
                if (tempWidth > rightOffset) {
                    //如果超出了一行的总长度,则要换成下一行
                    nowWidth = 0;
                    alreadyWriteLine++;
                    int writeY = startY + alreadyWriteLine * (textHeight + heightSpace);
                    if(draw)
                    {
                        GraphicsUtil.drawString(graphics2D,text.charAt(i)+"", font,Color.BLACK,new Point(startX + nowWidth, writeY));
                    }

                    nowWidth = oneWordWidth;
                } else {
                    //当前行长度足够,可以直接画
                    int writeY = startY + alreadyWriteLine * (textHeight + heightSpace);
                    if(draw)
                    {
                        GraphicsUtil.drawString(graphics2D,text.charAt(i)+"", font,Color.BLACK,new Point(startX + nowWidth, writeY));
                    }

                    nowWidth = tempWidth;
                }
            }
            lastY = startY + (alreadyWriteLine+1) * (textHeight + heightSpace);
        } else {
            //没有超过限制,直接画
            if(draw)
            {
                GraphicsUtil.drawString(graphics2D,text, font,Color.BLACK,new Point(startX , startY));
            }
            lastY = startY + textHeight + heightSpace;
        }
        return lastY;
    }

    /**
     *
     * @param graphics2D
     * @param text
     * @param font
     * @param maxWidth
     * @param startX
     * @param currentX
     * @param startY
     * @param heightSpace
     * @param indent
     * @return
     */
    public static int[] drawAndAppendString(Graphics2D graphics2D, String text,Font font, int maxWidth, int startX, int currentX, int startY, int heightSpace,int indent)
    {
        return drawAndAppendString(graphics2D, text, font, maxWidth, startX, currentX, startY, heightSpace, indent, 0, true);
    }

    public static int[] drawAndAppendString(Graphics2D graphics2D, String text,Font font, int maxWidth, int startX, int currentX, int startY, int heightSpace,int indent, int nextIndent)
    {
        return drawAndAppendString(graphics2D, text, font, maxWidth, startX, currentX, startY, heightSpace, indent, nextIndent, true);
    }

    public static int[] drawAndAppendString(Graphics2D graphics2D, String text,Font font, int maxWidth, int startX, int currentX, int startY, int heightSpace,int indent, boolean draw)
    {
        return drawAndAppendString(graphics2D, text, font, maxWidth, startX, currentX, startY, heightSpace, indent, 0, draw);
    }

    /**
     * 追加字符串,超过自动换行
     * @param graphics2D 地图对象
     * @param text  文本
     * @param font  字体
     * @param maxWidth  文本最宽x轴
     * @param startX    起始x轴
     * @param currentX  当前x轴(上一个文本的结尾坐标)
     * @param startY    起始y轴
     * @param heightSpace   行间距
     * @param indent 开头空格数
     * @return
     */
    public static int[] drawAndAppendString(Graphics2D graphics2D, String text,Font font, int maxWidth, int startX, int currentX, int startY, int heightSpace,int indent,int nextIndent,boolean draw)
    {
        int lastY = startY;
        int lastX = 0;
        StringBuilder sb = new StringBuilder();
        if(currentX == startX)
        {
            for(int i=0;i<indent;i++)
            {
                //全角空格
                sb.append('\u3000');
            }
        }
        sb.append(text);

        text = sb.toString();

        FontMetrics fontMetrics = graphics2D.getFontMetrics(font);
        Dimension dimension = FontUtil.getDimension(fontMetrics, text);

        int textLength = text.length();
        int totalWidth = (int) dimension.getWidth(); //一行的总长度,用于判断是否超出了范围

        int textHeight = (int) dimension.getHeight(); //计算一行的高度

        int rightOffset = maxWidth - currentX;
        if (totalWidth > rightOffset) {
            //总长度超过了整个长度限制
            int alreadyWriteLine = 0; //已经写了多少行
            int nowWidth = 0; //目前一行写的长度
            for (int i = 0; i < textLength; i++) {
                int nextIndentWidth = nextIndent * fontMetrics.charWidth('\u3000');

                int oneWordWidth = fontMetrics.charWidth(text.charAt(i)); //获取单个字符的长度
                int tempWidth = oneWordWidth + nowWidth; //判断目前的一行加上这个字符的长度是否超出了总长度
                if (tempWidth > rightOffset) {
                    //如果超出了一行的总长度,则要换成下一行
                    nowWidth = 0;
                    alreadyWriteLine++;
                    int writeY = startY + alreadyWriteLine * (textHeight + heightSpace);
                    if(draw)
                    {
                        GraphicsUtil.drawString(graphics2D,text.charAt(i)+"", font,Color.BLACK,new Point(startX + nowWidth + nextIndentWidth, writeY));
                    }
                    nowWidth = oneWordWidth;
                    currentX = startX + nextIndentWidth;
                    rightOffset = maxWidth - currentX;
                } else {
                    //当前行长度足够,可以直接画
                    int writeY = startY + alreadyWriteLine * (textHeight + heightSpace);
                    if(draw)
                    {
                        GraphicsUtil.drawString(graphics2D,text.charAt(i)+"", font,Color.BLACK,new Point(currentX + nowWidth, writeY));
                    }
                    nowWidth = tempWidth;
                }
            }
            lastY = startY + alreadyWriteLine * (textHeight + heightSpace);
            lastX = currentX + nowWidth;
        } else {
            //没有超过限制,直接画
            if(draw)
            {
                GraphicsUtil.drawString(graphics2D,text, font,Color.BLACK,new Point(currentX , startY));
            }
//            lastY = startY + textHeight + heightSpace;
            lastX = currentX + totalWidth;
        }
        return new int[]{lastX, lastY};
    }

    /**
     * 追加字符串,超过自动换行
     * @param graphics2D
     * @param as
     * @param font
     * @param maxWidth
     * @param startX
     * @param currentX
     * @param startY
     * @param heightSpace
     * @param indent
     * @return
     */
    public static int[] drawAndAppendString(Graphics2D graphics2D, AttributedString as,Font font, int maxWidth, int startX, int currentX, int startY, int heightSpace,int indent)
    {
        int lastY = startY;
        int lastX = 0;

        AttributedCharacterIterator iter = as.getIterator();
        int limit = iter.getRunLimit();

        //构造字符串
        StringBuilder textBuilder = new StringBuilder();
        while (iter.getIndex() < limit)
        {
            textBuilder.append(iter.current());
            iter.next();
        }

        String text = textBuilder.toString();

        StringBuilder sb = new StringBuilder();
        if(currentX == startX)
        {
            for(int i=0;i<indent;i++)
            {
                //全角空格
                sb.append('\u3000');
            }
        }
        sb.append(text);

        text = sb.toString();

        FontMetrics fontMetrics = graphics2D.getFontMetrics(font);
        Dimension dimension = FontUtil.getDimension(fontMetrics, text);

        int textLength = text.length();
        int totalWidth = (int) dimension.getWidth(); //一行的总长度,用于判断是否超出了范围

        int textHeight = (int) dimension.getHeight(); //计算一行的高度

        //距离右边
        int rightOffset = maxWidth - currentX;
        //从头遍历
        iter.setIndex(0);
        if (totalWidth > rightOffset) {
            //总长度超过了整个长度限制
            int alreadyWriteLine = 0; //已经写了多少行
            int nowWidth = 0; //目前一行写的长度

            //遍历字符
            while (iter.getIndex() < limit)
            {
                //获取单个富文本
                AttributedString tempAs = new AttributedString(String.valueOf(iter.current()));
                AttributedCharacterIterator tempIter = tempAs.getIterator();
                tempAs.addAttributes(iter.getAttributes(), tempIter.getBeginIndex(), tempIter.getEndIndex());

                int oneWordWidth = fontMetrics.charWidth(iter.current()); //获取单个字符的长度
                int tempWidth = oneWordWidth + nowWidth; //判断目前的一行加上这个字符的长度是否超出了总长度
                if (tempWidth > rightOffset) {
                    //如果超出了一行的总长度,则要换成下一行
                    nowWidth = 0;
                    alreadyWriteLine++;
                    int writeY = startY + alreadyWriteLine * (textHeight + heightSpace);
//                    GraphicsUtil.drawString(graphics2D,text.charAt(i)+"", font,Color.BLACK,new Point(startX + nowWidth, writeY));

                    graphics2D.drawString(tempAs.getIterator(), startX + nowWidth, writeY);
                    nowWidth = oneWordWidth;
                    currentX = startX;
                    rightOffset = maxWidth;
                } else {
                    //当前行长度足够,可以直接画
                    int writeY = startY + alreadyWriteLine * (textHeight + heightSpace);
//                    GraphicsUtil.drawString(graphics2D,text.charAt(i)+"", font,Color.BLACK, new Point(currentX + nowWidth, writeY));
                    graphics2D.drawString(tempAs.getIterator(), currentX + nowWidth, writeY);
                    nowWidth = tempWidth;
                }
                iter.next();
            }
            lastY = startY + alreadyWriteLine * (textHeight + heightSpace);
            lastX = currentX + nowWidth;
        } else {
            //没有超过限制,直接画
            graphics2D.drawString(iter, currentX, startY);
            lastX = currentX + totalWidth;
        }
        return new int[]{lastX, lastY};
    }

    /**
     * 计算
     * @param graphics2D
     * @param text
     * @param font
     * @param maxWidth
     * @param currentX
     * @return
     */
    public static double calculatePercentOfMargin(Graphics2D graphics2D, String text, Font font, int maxWidth, int currentX)
    {
        FontMetrics fontMetrics = graphics2D.getFontMetrics(font);
        Dimension dimension = FontUtil.getDimension(fontMetrics, text);
        int totalWidth = (int) dimension.getWidth();
        int rightOffset = maxWidth - currentX;
        BigDecimal bigDecimal = new BigDecimal((float)totalWidth / rightOffset).setScale(2, BigDecimal.ROUND_DOWN);
        return bigDecimal.doubleValue();
    }

    /**
     * 生成证书base64
     *
     */
    public static String createCertificateImageBase64(BufferedImage bufferedImage) {

        if (null == bufferedImage) {
            return "";
        }
        String result = "";
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(bufferedImage, "png", outputStream);
            result = Base64.encode(outputStream.toByteArray());

            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

(3)上传文件工具类

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.UUID;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;


/**
 * 文件上传工具类
 *
 * @author ruoyi
 */
public class FileUploadUtils
{
    /**
     * 默认大小 100M
     */
    public static final long DEFAULT_MAX_SIZE = 100 * 1024 * 1024;

    /**
     * 默认的文件名最大长度 100
     */
    public static final int DEFAULT_FILE_NAME_LENGTH = 100;

    /**
     * 默认上传的地址
     */
    private static String defaultBaseDir = RuoYiConfig.getProfile();

    public static void setDefaultBaseDir(String defaultBaseDir)
    {
        FileUploadUtils.defaultBaseDir = defaultBaseDir;
    }

    public static String getDefaultBaseDir()
    {
        return defaultBaseDir;
    }

    /**
     * 以默认配置进行文件上传
     *
     * @param file 上传的文件
     * @return 文件名称
     * @throws Exception
     */
    public static final String upload(MultipartFile file) throws IOException
    {
        try
        {
            return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 根据文件路径上传
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @return 文件名称
     * @throws IOException
     */
    public static final String upload(String baseDir, MultipartFile file) throws IOException
    {
        try
        {
            return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }
    /**
     * 二维码专用
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @return 文件名称
     * @throws IOException
     */
    public static final String upCodeload(String baseDir, MultipartFile file) throws IOException
    {
        try
        {
            return uploadCode(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        }
        catch (Exception e)
        {
            throw new IOException(e.getMessage(), e);
        }
    }
    public static final String uploadCode(String baseDir, MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
            InvalidExtensionException
    {
        int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
        {
            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
        }

        assertAllowed(file, allowedExtension);

        String fileName = file.getOriginalFilename();

        String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
        file.transferTo(Paths.get(absPath));
        return getPathFileName(baseDir, fileName);
    }

    /**
     * 文件上传
     *
     * @param baseDir 相对应用的基目录
     * @param file 上传的文件
     * @param allowedExtension 上传文件类型
     * @return 返回上传成功的文件名
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws FileNameLengthLimitExceededException 文件名太长
     * @throws IOException 比如读写文件出错时
     * @throws InvalidExtensionException 文件校验异常
     */
    public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException,
            InvalidExtensionException
    {
        int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length();
        if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH)
        {
            throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH);
        }

        assertAllowed(file, allowedExtension);

        String fileName = extractFilename(file);

        String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath();
        file.transferTo(Paths.get(absPath));
        return getPathFileName(baseDir, fileName);
    }

    /**
     * 编码文件名
     */
    public static final String extractFilename(MultipartFile file)
    {
        return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
                FilenameUtils.getBaseName(UUID.randomUUID().toString()), Seq.getId(Seq.uploadSeqType), getExtension(file));
//        return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
//                FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));
    }

    public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException
    {
        File desc = new File(uploadDir + File.separator + fileName);

        if (!desc.exists())
        {
            if (!desc.getParentFile().exists())
            {
                desc.getParentFile().mkdirs();
            }
        }
        return desc;
    }

    public static final String getPathFileName(String uploadDir, String fileName) throws IOException
    {
        int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
        String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
        return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
    }

    /**
     * 文件大小校验
     *
     * @param file 上传的文件
     * @return
     * @throws FileSizeLimitExceededException 如果超出最大大小
     * @throws InvalidExtensionException
     */
    public static final void assertAllowed(MultipartFile file, String[] allowedExtension)
            throws FileSizeLimitExceededException, InvalidExtensionException
    {
        long size = file.getSize();
        if (size > DEFAULT_MAX_SIZE)
        {
            throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024);
        }

        String fileName = file.getOriginalFilename();
        String extension = getExtension(file);
        if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension))
        {
            if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension,
                        fileName);
            }
            else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION)
            {
                throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension,
                        fileName);
            }
            else
            {
                throw new InvalidExtensionException(allowedExtension, extension, fileName);
            }
        }

    }

    /**
     * 判断MIME类型是否是允许的MIME类型
     *
     * @param extension
     * @param allowedExtension
     * @return
     */
    public static final boolean isAllowedExtension(String extension, String[] allowedExtension)
    {
        for (String str : allowedExtension)
        {
            if (str.equalsIgnoreCase(extension))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取文件名的后缀
     *
     * @param file 表单文件
     * @return 后缀名
     */
    public static final String getExtension(MultipartFile file)
    {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension))
        {
            extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
        }
        return extension;
    }

    /**
     * 根据url下载文件到本地
     * @param url
     * @param localFilePath 含文件名
     * @return 1:成功 0:失败
     */
    public static int downloadFileFromUrl(String url, String localFilePath) {

        try {
            URL httpUrl = new URL(url);
            File f = new File(localFilePath);
            FileUtils.copyURLToFile(httpUrl, f);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
        return 1;
    }

    public static int fileExists(String path)
    {
        File file = new File(path);
        if(file.isFile()) {
            return 1; // 是文件
        } else if(file.isDirectory()) {
            if(Objects.requireNonNull(file.list()).length>0)
                return 2;   // 非空文件夹
            else
                return 3;   // 空文件夹
        }
        return 0; // 路径不存在
    }

    /**
     * 根据url下载文件到本地
     * @param url
     * @param localFilePath 含文件名
     * @return 1:成功 0:失败
     */
    public static int downloadFileFromUrl2(String url, String localFilePath) {
        int res = 0;
        int fileExists = FileUploadUtils.fileExists(localFilePath);
        // 下载文件,先查询文件是否已存在
        if (StringUtils.isNotBlank(url) && fileExists == 0) {
            res = FileUploadUtils.downloadFileFromUrl(url, localFilePath);
        }
        return res;
    }
}

3、绘制证书

    public static void main(String args[]) {

        final int alignRight = 1;   // 对齐右侧
        final int alignCenter = 0;  // 居中

        // 背景图输入流
        InputStream backgroundInputStream = null;
        try {
            // 获取背景
            String path = "C:\\Users\\Dell\\Desktop\\图片\\证书模板.jpg";

            // 构造背景
            backgroundInputStream = new FileInputStream(path);

			// 按需压缩
            int backgroundWidth = 1000;     //宽度像素
            Img backGround = Img.from(backgroundInputStream);
            Img backgroudScale = backGround.scale(backgroundWidth, 700);// 按照目标宽度、高度缩放图像
            BufferedImage backgroudBuffer = ImgUtil.copyImage(backgroudScale.getImg(), BufferedImage.TYPE_INT_ARGB);    // 表示包含8位RGBA颜色组件的图像整数像素

            // 以背景图作为画布
            Graphics2D graphics = GraphicsUtil.createGraphics(backgroudBuffer, null);

            // 设置字体大小
            baseFont = baseFont.deriveFont(20f);
            float originNormalFontSize = 20f;
            normalFont = normalFont.deriveFont(originNormalFontSize);   // 正文字体
            ltBoldFont = ltBoldFont.deriveFont(originNormalFontSize);   //加粗字体

            graphics.setFont(baseFont);

            String variableCoordinates = "[{name:\"Java123\",x:\"374\",y:\"298\"},{name:\"小李\",x:\"570\",y:\"299\"},{name:\"Graphics杯\",x:\"320\",y:\"351\"}]";
            // 遍历把字全部填到地图上
            List<VariableCoordinatesVo> variableCoordinatesList = JSONUtil.toList(JSONUtil.toJsonStr(variableCoordinates), VariableCoordinatesVo.class);
            for (VariableCoordinatesVo variableCoordinatesVo : variableCoordinatesList) {

                int align = alignCenter;   // 居中写
                Integer x = variableCoordinatesVo.getX();
                Integer y = variableCoordinatesVo.getY();
                String name = variableCoordinatesVo.getName();
                // 根据x,y轴居中写
                HuGraphics.drawString(graphics, name, ltBoldFont, x, y, align);

            }
            // 收尾
            graphics.dispose();

			// 比例压缩到一半
            backgroudBuffer = Thumbnails.of(backgroudBuffer).scale(0.5f).asBufferedImage();
			
			// 图片base64码
    		String baseImg = createCertificateImageBase64(backgroudBuffer);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

结果图展示:

压缩绘制后的图片

三、保存证书文件

生成结束后,可以一次性将图片的base64返回给前端,但是绘制图片是一个比较占用内存的操作,当并发量较大时应该减少绘制的频率。可以将生成的证书图片转为文件保存至服务器,再返回文件的url给到前端。

public static void main(String args[]) {
	// ... 
	
	// 比例压缩到一半
	backgroudBuffer = Thumbnails.of(backgroudBuffer).scale(0.5f).asBufferedImage();

	// 1.图片base64码
    String baseImg = createCertificateImageBase64(backgroudBuffer);
	
	// 2.将图片写入ByteArrayOutputStream流,转为字节数组
	ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
	ImageIO.write(backgroudBuffer, "png", byteArrayOutputStream);
	byte[] bytes = byteArrayOutputStream.toByteArray();
}

1.上传至服务器

使用图片base64作为入参

String localFilePath = "C:\\Users\\Dell\\Desktop\\图片\\生成证书.png";
FileUploadUtils.downloadFileFromUrl2(URLDecoder.decode(srcUrl), localFilePath);

2.上传至minio

使用字节数组作为入参

/**
     * 将输出流转为输入流,上传至minio
     *
     * @param bytes
     * @param bizPath 文件夹路径
     * @param fileName 文件名
     */
    public static String outPutStreamUpload(byte[] bytes, String bizPath, String customBucket, String fileName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InternalException, NoResponseException, InvalidBucketNameException, XmlPullParserException, ErrorResponseException, RegionConflictException, InvalidArgumentException {

        // 创建inputStream流
        InputStream inputStream = new ByteArrayInputStream(bytes);//将inputStream流写成文件(查看文件内容是否正确)

        // 上传至minio
        String fileUrl = "";
        String newBucket = bucketName;
        if (oConvertUtils.isNotEmpty(customBucket)) {
            newBucket = customBucket;
        }
        // 初始化minio
        initMinio(minioUrl, minioName, minioPass);
        if (!minioClient.bucketExists(bucketName)) {
            // 创建一个名为ota的存储桶
            minioClient.makeBucket(bucketName);
        }
        String objectName = bizPath + "/" + fileName;
        minioClient.putObject(newBucket, bizPath + "/" + fileName, inputStream, inputStream.available(), "application/octet-stream");
        inputStream.close();
        fileUrl = minioUrl + newBucket + "/" + objectName;

        // 获取文件大小
        ObjectStat objectStat = minioClient.statObject(newBucket, objectName);
        long fileSize = objectStat.length();
//        log.info("读取minio文件:桶名:{},文件名:{},获取文件大小:{}", minioBucketName, relativePath, fileSize);

        String ofileName = fileName;
        String suffix = "";
        if (StringUtils.isNotEmpty(ofileName)) {
            suffix = ofileName.substring(ofileName.lastIndexOf(".") + 1);
            ofileName = ofileName.substring(0, ofileName.lastIndexOf("."));
        }

        return fileUrl;
    }
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值