记一次后端生成图片返回前端预演

2020-12-30 04:30:18  晓掌柜  版权声明:本文为站长原创文章,转载请写明出处


一、背景

    在之前的文章中有提到过说前端生成图片返回到后端:链接如下:

        (html2canvas简明使用教程脚本打印页面指定区域内容Java条形码标签生成并打印示例)。

    除此之外最近有需求可能会用到后端生成图片返回前端,于是写一个demo先记录,等正式使用时再进行完善。


二、整体实现

    2.1、实现思路

        ① 创建图片缓冲区

        ② 使用Graphics2D来绘制图形

        ③ 使用ImageIO来保存为图片(可以时临时文件,也可以是保存到本地)

        ④ 转成文件为Base64数据返回给前端

    

    2.2、附上核心代码部分        


     import sun.misc.BASE64Encoder;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author XA
* date 2020/12/30 20:20
* description: 后端生成图片返回给前端Base64
*/

public class BuildImg {

public static void main(String[] args) {
/* 图片宽 */
int width = 820;
/* 图片高 */
int height = 600;
String titleStr = "导出表格图片测试";
String zhangdanzhouqiStr = "2020年12月30日20:00:35";
String zhangdantianshuStr = "666";
String bengedinggonglvStr = "200";
String bengbianpingjienengyunxingyongdianliangStr = "100";
String dianjiaStr = "24";
String pingjunjienenglvStr = "50%";
String daizhifujingeStr = "1000元";
String bengyunxingzongshichangStr = "656";
String benggongpingyongdianliangStr = "13";
String jieshengdianliangStr = "77";
String jieshengjineStr = "1000元";

/* 得到图片缓冲区 */
/* INT精确度达到一定,RGB三原色,高度70,宽度150 */
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

/* 得到它的绘制环境(这张图片的笔) */
Graphics2D g2 = (Graphics2D) bi.getGraphics();
/* 设置背景颜色 */
g2.setColor(Color.WHITE);
/* 填充整张图片(其实就是设置背景颜色) */
g2.fillRect(0, 0, width, height);
/* 设置字体颜色 */
g2.setColor(Color.black);
/* 边框加粗 */
g2.setStroke(new BasicStroke(2.0f));
/* 画边框就是黑边框 */
g2.drawRect(1, 1, width - 2, height - 2);

/* 从上到下第二个横线 */
g2.drawLine(0, 80, 820, 80);
/* 边框不需要加粗 */
g2.setStroke(new BasicStroke(0.0f));
/* 从上到下第三个横线 */
g2.drawLine(0, 154, 820, 154);
/* 从上到下第四个横线 */
g2.drawLine(0, 228, 820, 228);
/* 从上到下第5个横线 */
g2.drawLine(0, 302, 820, 302);
/* 从上到下第6个横线 */
g2.drawLine(0, 376, 820, 376);
/* 从上到下第7个横线 */
g2.drawLine(0, 451, 820, 451);
/* 从上到下第8个横线 */
g2.drawLine(0, 525, 820, 525);

/* 从左到右第二个竖线 */
g2.drawLine(180, 80, 180, 600);
/* 从左到右第三个竖线 */
g2.drawLine(390, 154, 390, 451);
/* 从左到右第四个竖线 */
g2.drawLine(574, 154, 574, 451);

/* 设置标题的字体,字号,大小 */
Font titleFont = new Font("宋体", Font.BOLD, 30);
g2.setFont(titleFont);
String markNameStr = titleStr;
/* 抗锯齿 */
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
/* 计算文字长度,计算居中的X点坐标 */
FontMetrics fm = g2.getFontMetrics(titleFont);
int titleWidth = fm.stringWidth(markNameStr);
/* 向左移动35个单位以居中 */
int titleWidthX = (width - titleWidth) / 2 - 35;
g2.drawString(markNameStr, titleWidthX, 45);

g2.setFont(new Font("宋体", Font.BOLD, 20));
g2.drawString("时间", 33, 125);
g2.drawString(zhangdanzhouqiStr, 230, 125);

g2.drawString("数量", 33, 200);
g2.drawString(zhangdantianshuStr, 230, 200);
g2.drawString("效能", 33, 274);
g2.drawString(bengedinggonglvStr, 230, 274);

g2.drawString("用量", 33, 338);
g2.drawString("重量", 33, 360);
g2.drawString(bengbianpingjienengyunxingyongdianliangStr, 230, 345);

g2.drawString("单价", 33, 423);
g2.drawString(dianjiaStr, 230, 423);

g2.drawString("平均值", 33, 496);
g2.drawString(pingjunjienenglvStr, 230, 496);

/* 待支付金额 */
g2.drawString("金额", 33, 568);
/* 待支付金额的值 */
g2.drawString(daizhifujingeStr, 230, 568);

g2.drawString("总时长", 420, 200);
g2.drawString(bengyunxingzongshichangStr, 630, 200);

g2.drawString("数值", 420, 265);
g2.drawString("数量", 420, 287);
g2.drawString(benggongpingyongdianliangStr, 630, 274);

g2.drawString("量", 420, 348);
g2.drawString(jieshengdianliangStr, 630, 345);

g2.drawString("费用", 420, 423);
g2.drawString(jieshengjineStr, 630, 423);

/* 释放对象 */
g2.dispose();
try {
/* 创建临时文件用以保存图片数据 */
File file1 = null;
file1 = File.createTempFile("temp", null);

/* 保存图片 JPEG表示保存格式 */
ImageIO.write(bi, "JPEG", file1);
/* 也可以保存为本地文件 */
/* ImageIO.write(bi, "JPEG", new FileOutputStream("D:/a.jpg")); */
String imgBase64 = getBase64(file1);
System.out.println(imgBase64);
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 功能描述: 获取文件的Base64编码
* Param: [file]
* Return: java.lang.String
*/

private static String getBase64(File file){
String imgStr = "";
try {
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[(int) file.length()];
int offset = 0;
int numRead = 0;
while (offset < buffer.length && (numRead = fis.read(buffer, offset, buffer.length - offset)) >= 0) {
offset += numRead;
}

if (offset != buffer.length) {
throw new IOException("Could not completely read file "
+ file.getName());
}
fis.close();
BASE64Encoder encoder = new BASE64Encoder();
imgStr = encoder.encode(buffer);
} catch (Exception e) {
e.printStackTrace();
}
return "data:image/jpeg;base64,"+imgStr;
}

}


    PS: 如果想要插入图片也可以使用下面的方式


        /* 写入图片 */
List<String> ossList = new ArrayList<>();
ossList.add("http://XXXX623133213081.jpg");
ossList.add("http://XXX23133220123.jpg");
ossList.add("http://XXX0210623143822891.jpg");
ossList.add("http://XXX213081.jpg");
ossList.add("http://XXX33220123.jpg");
ossList.add("http://XXX23143822891.jpg");

for(int i=0;i<ossList.size();i++){ /* 可以读取磁盘文件 */ /* InputStream inputStream = new FileInputStream(new File("D:/" + (ii+1) + ".jpg")); */ /* 也可以是一个网络路径 */ InputStream inputStream = getImageStream(ossList.get(i));
BufferedImage bufferedImage = ImageIO.read(inputStream);
g2.drawImage(bufferedImage,5,i * 810 + 5,590,800,null);
}

    

 
   /**
* 功能描述: 根据图片的在线地址获取InputStream
* Param: [url]
* Return: java.io.InputStream
*/
private static InputStream getImageStream(String url) {
try {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setReadTimeout(5000);
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
InputStream inputStream = connection.getInputStream();
return inputStream;
}
} catch (IOException e) {
System.out.println("获取网络图片出现异常,图片路径为:" + url);
e.printStackTrace();
}
return null;
}


三、效果展示


    3.1、拿到Base64数据

        大致如下:data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBXXX...


    3.2、在前端中显示效果如下


          

四、后记

    ① 这只是一个雏形记录,不代表最终实现

    ② 这之中会有很多坏味道,比如Base64的传输数据量大的问题

    ③ 临时记录一下,如果最终使用,或有更好的方式持续更新


五、写在最后面的话


    5.1、数据传输方案的考量

        正式上线之后,考虑到传输性能和存储等因素,并没有使用base64作为数据媒介

    5.2、最终处理方案

        最终的处理方式为:上传到oss,然后返回路径给到前端处理

    5.3、外文兼容性问题

        在地址的部分因为我们有泰文,所以遇到了不能泰文的问题,

        检查之后发现是定义字体时选了宋体导致的。最终解决是在设置字体时不明确定义,

        使用jdk的默认字体即可!

    附:JDK在声明字体时的部分源码

     public Font(String name, int style, int size) {
this.name = (name != null) ? name : "Default";
this.style = (style & ~0x03) == 0 ? style : 0;
this.size = size;
this.pointSize = size;
}

    

    5.4、部署到Centos上字体问题

        ① 在确认文件格式无误后,部署到线上环境,发现及时使用了默认字体也会出现:

            泰文无法解析出来!!!

            不用想也是字体库的问题,下面附解决方案。

        ② 打包window上的字体并上传

            文件位于:C:\Windows\Fonts

            把它打包成zip文件,上传到:/usr/share/fonts/

        ③ 建立字体索引并更新字体缓存

            mkfontscale
            mkfontdir
            fc-cache

        ④ 使用 fc-list查询字体库列表,确认无误后重启服务即可


    更多精彩请持续关注:guangmuhua.com


最新评论: