之前有遇到一个问题

    问题背景:项目中,有一个功能,管理端可以将客户创建的小程序码下载到本地,方便客户将对应门店的小程序码打印出来并张贴到门店,做门店的引流和会员入会。

    具体问题:当小程序码的数量较少的时候,我们是后端将小程序码的分组信息和小程序码的图片以树的数据结构形式,返回给前端,由前端拿到分组信息和小程序图片链接,在前端进行下载小程序码图片,并将分组信息拼接在小程序码的下方,类似这样:

            但是,当这个门店的结构复杂之后,小程序码的数量也多了起来,由前端来下载就显得非常让人焦灼了,前端只能使用下载的这台电脑的性能来一张一张的下载小程序码并拼接门店的信息,1000多张小程序码的话,就需要10分钟左右的等待时间,有的客户的电脑性能比较差的话,干脆就没办法下载,怎么办呢?

        一句话,放后端并行下载呗,然后直接返回zip包的流数据文件给前端不就行了。。。

        当时接到这个任务,我也天真的认为,搞个线程并行下载,然后打包不就OK了么,能有多费事呢?服务器随随便便不就16核+64G的配置,下载个文件就算网络差点,千把个图片还不是分分钟的事儿嘛,领导面前胸口拍得梆梆响,小事一桩嘛~~~

        下载倒是好说,并发 CountDownLatch cdl = new CountDownLatch(size); 控制下下载的次序下载完再一起打包。。。

        但是,把文字怎么搞到这张小程序码图片的下面呢,又不能拉伸这张图片,那就要把文字先转成图片(跟小程序码图片宽度保持一致)

        a.先把远程的图片下载到本地

String localFilePath = "D:\\Download\\0402" + File.separator;
String localFileName = "test3.png";
downloadFile("http://0.0.0.0:8080/photo/gh_ff959c80f0d7_1280.jpg", localFilePath, localFileName);


/**
 * 下载远程文件并保存到本地
 */
public static void downloadFile(String remoteFilePath, String localFilePath, String fileName) {
        FileUtil.mkdir(localFilePath);
        ReadableByteChannel rbc = null;
        FileOutputStream fos = null;
        try {
            URL website = new URL(remoteFilePath);
            rbc = Channels.newChannel(website.openStream());
            fos = new FileOutputStream(localFilePath + fileName);
            fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
        } catch (Exception e) {
            log.warn(e.getMessage());
        } finally {
            IOUtils.closeQuietly(fos);
            IOUtils.closeQuietly(rbc);
        }

    }

        b.将第一行文字和第二行文字转换成跟小程序码图片一样宽度的图片

/**
     * 小程序码 导出组名 二维码名 字体
     */
    public static final String SOURCE_HAN_SANS = "思源黑体 CN Regular";
    /**
     * 小程序码 导出 二维码名 字号
     */
    public static final int FONT_SIZE_TITLE = 24;
    /**
     * 小程序码 导出组名 字号
     */
    public static final int FONT_SIZE_GROUP_NAME = 14;
    /**
     * 小程序码 导出二维码图片 缩略图 宽度
     */
    public static final int MA_QR_CODE_IMAGE_WIDTH = 552;
    /**
     * 小程序码 导出二维码图片 缩略图 高度
     */
    public static final int MA_QR_CODE_IMAGE_HEIGHT = 552;
    /**
     * 小程序码 文字生成图片 背景高度
     */
    public static final int BACK_GROUND_IMAGE_HEIGHT = 35;
    /**
     * 小程序码 导出二维码名 每行长度
     */
    public static final int MA_QR_CODE_SPLIT_SIZE = 21;
    /**
     * 小程序码 导出组名 每行长度
     */
    public static final int MA_GROUP_SPLIT_SIZE = 38;

//第一行文字
String fileName = "ZSHMD上海市浦东新区东方体育中心万达购物中心店";
String targetFile = localFilePath + "test3_t1.png";
TextToImage.textToImage(TextToImage.ImageContent.buildOf(MA_QR_CODE_IMAGE_WIDTH, MA_QR_CODE_SPLIT_SIZE, Color.BLACK, SOURCE_HAN_SANS, FONT_SIZE_TITLE, fileName, targetFile));
//第二行文字
String groupFullName = "全部-中国-上海-浦东新区-三林镇";
String groupTargetFile = localFilePath + "test3_t2.png";
TextToImage.textToImage(TextToImage.ImageContent.buildOf(MA_QR_CODE_IMAGE_WIDTH, MA_GROUP_SPLIT_SIZE, Color.GRAY, SOURCE_HAN_SANS, FONT_SIZE_GROUP_NAME, groupFullName, groupTargetFile));

工具类方法:

/**
     * 将文字转换为png图片
     */
    public static void textToImage(ImageContent content) throws IOException {
        //小程序码 文字生成图片 背景高度
        String contentText = content.getText();
        if (StringUtils.isEmpty(contentText)) {
            return;
        }

        String[] texts = contentText.split("(?<=\\G.{" + content.getSplitSize() + "})");
        int height = texts.length * BACK_GROUND_IMAGE_HEIGHT;
        //创建图片
        int width = content.getWidth();
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = bufferedImage.createGraphics();
        //设置背景
        graphics.fillRect(0, 0, width, height);
        //定义字体
        Font font = new Font(content.getFontName(), Font.PLAIN, content.getFontSize());
        // 防止生成的文字带有锯齿
        graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        //设置颜色
        graphics.setColor(content.getColor());
        //设置字体
        graphics.setFont(font);
        //写入 (getWordWidth 计算该字体文本的长度) 居中
        Lists.mutable.of(texts).forEachWithIndex((x, i) ->
                graphics.drawString(x, (width - getWordWidth(font, x)) / 2, 20 + 30 * i)
        );
        graphics.dispose();
        ImageIO.write(bufferedImage, PNG, new File(content.getTargetFile()));
    }

工具类-内部类:

@Data
    public static class ImageContent{
        private int width;
        private int splitSize;
        private Color color;
        private String fontName;
        private int fontSize;
        private String text;
        private String targetFile;

        public static ImageContent buildOf(int maQrCodeImageWidth, int maQrCodeSplitSize, Color color, String fontName, int fontSize, String text, String targetFile) {
            ImageContent content = new ImageContent();
            content.setWidth(maQrCodeImageWidth);
            content.setSplitSize(maQrCodeSplitSize);
            content.setColor(color);
            content.setFontName(fontName);
            content.setFontSize(fontSize);
            content.setText(text);
            content.setTargetFile(targetFile);

            return content;
        }
    }

    @Getter
    @AllArgsConstructor
    public enum SpliceType{
        /** */
        TRANSVERSE("横向"),
        PORTRAIT("纵向");

        private final String desc;
    }

        c.将小程序码图片生成552x552大小的缩略图

//生成图片的缩略图 552x552
Thumbnails.of(localFilePath + localFileName).size(MA_QR_CODE_IMAGE_WIDTH, MA_QR_CODE_IMAGE_HEIGHT).keepAspectRatio(false).toFile(localFilePath + "test3_t0.png");
        

maven依赖:
<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
<dependency>
    <groupId>net.coobird</groupId>
    <artifactId>thumbnailator</artifactId>
    <version>0.4.8</version>
</dependency>

        d.拼接图片,生成想要的那种图片结构

TextToImage.mergeImage(new String[]{localFilePath + "test3_t0.png", localFilePath + "test3_t1.png", localFilePath + "test3_t2.png"}
                , TextToImage.SpliceType.PORTRAIT, localFilePath + File.separator + "test3.png");


    /**
     * @param files 要拼接的图片列表
     * @param type  1 横向拼接, 2 纵向拼接
     *              图片拼接 (注意:必须两张图片长宽一致)
     */
    public static void mergeImage(String[] files, SpliceType type, String targetFile) {
        int len = files.length;
        if (len < 1) {
            log.warn("图片数量小于1");
            return;
        }
        File[] src = new File[len];
        BufferedImage[] images = new BufferedImage[len];
        int[][] imageArrays = new int[len][];
        for (int i = 0; i < len; i++) {
            try {
                src[i] = new File(files[i]);
                images[i] = ImageIO.read(src[i]);
            } catch (Exception e) {
                log.warn("{}", e.getMessage(), e);
            }
            int width = images[i].getWidth();
            int height = images[i].getHeight();
            imageArrays[i] = new int[width * height];
            imageArrays[i] = images[i].getRGB(0, 0, width, height, imageArrays[i], 0, width);
        }
        int newHeight = 0;
        int newWidth = 0;
        for (BufferedImage image : images) {
            // 横向
            if (SpliceType.TRANSVERSE == type) {
                newHeight = Math.max(newHeight, image.getHeight());
                newWidth += image.getWidth();
            }
            // 纵向
            if (SpliceType.PORTRAIT == type) {
                newWidth = Math.max(newWidth, image.getWidth());
                newHeight += image.getHeight();
            }
        }
        if (SpliceType.TRANSVERSE == type && newWidth < 1) {
            return;
        }
        if (SpliceType.PORTRAIT == type && newHeight < 1) {
            return;
        }

        // 生成新图片
        try {
            BufferedImage imageNew = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
            int height = 0;
            int width = 0;
            for (int i = 0; i < images.length; i++) {
                if (SpliceType.TRANSVERSE == type) {
                    imageNew.setRGB(width, 0, images[i].getWidth(), newHeight, imageArrays[i], 0, images[i].getWidth());
                    width += images[i].getWidth();
                }
                if (SpliceType.PORTRAIT == type) {
                    imageNew.setRGB(0, height, newWidth, images[i].getHeight(), imageArrays[i], 0, newWidth);
                    height += images[i].getHeight();
                }
            }
            //输出想要的图片
            ImageIO.write(imageNew, PNG, new File(targetFile));
        } catch (Exception e) {
            log.warn("{}", e.getMessage(), e);
        }
    }

        注意,如果部署Linux上之后,可能会发现文字的位置是空的,那就需要在项目根目录下安装一下字体哦。

javafx在图片上标点 java怎么在图片上加文字_图片拼接

 

//始终都删除中间生成的临时文件
Lists.mutable.of("test3_t0.png", "test3_t1.png", "test3_t2.png").forEach(FileUtil::del);

         好了,到此,这个问题已经解决了,并不知道有大佬有没有更好的办法呢,还请不吝赐教呀~