Java实战之每日海报

前言

使用java生成每日海报。
java

项目起因是巧合下遇到了一篇很棒的文档,说的是用程序来实现每日生成一个海报。如果之后加上自动发布的功能,简直就是太棒了啊!

样例图如下:daily_poster_20240112

每日海报

思路

  1. 访问某词站的API获取网络图片,为的就是他已经裁剪好的大小。
  2. 加载个人品牌二维码,这里我是以个人的微信公众号的二维码。
  3. 获取某词爸的文字内容,此处使用Jsoup解析Html结果后取最新的句子。
  4. 利用 Graphics2D 在海报上打印中英文对照语、绘制个人专属二维码,打印当前日期
  5. 运行项目即可,也可以后期达成Jar包,单独运行一次就能生成一个。

我知道,读者你肯定还是希望能够让项目按时间自动生成,我不仅要求这些,还要能自动发布微信公众号和博客呢。

那些都是后期功能了,本期就关注于生成最关键的图片。

数据来源

因为时间过去很久了,原文中很多的Api已经不能够继续使用了。所以这里简单记录一下我所使用的数据来源。

某词爸

遗憾的是,通过网页访问GET请求只能够得到文字,无法拿到图片了

http://jinjin.online/tools/word_everyday/show.php?page_num=1

某词站

可以通过POST请求访问地址获取。

http://learn.baicizhan.com/api/activity/sentence/list

需要注意的是,要在请求头中加上Cookie,并且其中要加上个人认证的access_token字段才可以获取到数据。

所以如果读者想要复现的话,就需要在自己的手机上登入自己的账户,然后通过抓包的方式获取到指定数据才可以。

具体的抓包方式教程我附在文末了。

图片下载下来之后,可以确认分辨率是1125x1500,对我而言已经足够了。

功能实现

获取网络图片

首先通过抓包拿到自己的手机请求每日图片的Token,为了防止某些不必要的误会,这里不展开讲了。

public String getInfoList() {return HttpUtils.doPost(urlConfig.getBczUrl(), "{\"size\":20}", HttpUtils.headersMap);
}

得到的结果是类似于这样的JSON体:

{"date":1704902400000,"sentence":{"en":"What is an ocean but a multitude of drops.","cn":"不积小流,无以成江海。"},"backgroundImg":"https://7n.bczcdn.com/r/qeb7xm3jjhp9zhmtz8cgv3d254l24xh5.png","dakaNum":7817,"sentenceSource":"-《云图》","collected":false,"id":597,"audio":"https://vol.bczcdn.com/r/us_What_is_20221001152932680_bc72af44e192cb6ee36c.mp3","daka":false
}

而我只需要其中的背景图片地址,将他保存为临时文件。

public File getBczInfoImage(BczDTO bczDTO) {String backgroundImg = bczDTO.getBackgroundImg();try {// 获取输入流对象InputStream inputStream = bczService.getBczInfoImage(bczDTO);// 存储为临时文件return FileUtils.createTmpFile(inputStream,dirsConfig.getImagesDir(),DateUtil.formatDate(new Date(), DateUtil.YYYYMMDD) + "_",backgroundImg.substring(backgroundImg.lastIndexOf(Constant.DOT)));} catch (Exception e) {log.error("获取某词站临时图片发生错误:{},{}", e.getMessage(), e);throw new ServiceException("获取某词站临时图片发生错误");}}

个人二维码

至于个人二维码我是用的微信公众号的二维码。

直接将图片放到了项目里,也可以通过项目配置文件指定绝对路径来找到你自己的二维码图片。

public File getQcCodeFile(String qcCodeName) {return new File(qcCodeName);
}

获取网络文字

public Sentence getJscbInfo() {Sentence sentence = new Sentence();String html = jscbService.getInfo();//用Jsoup解析htmlDocument document = Jsoup.parse(html);//像js一样,通过class获取列表下的所有Elements postItems = document.getElementsByClass("list-group");postItems = postItems.get(postItems.size()-1).getElementsByTag("font");// 此处是英文sentence.setEn(postItems.get(1).text());// 此处是中文sentence.setCn(postItems.get(2).text());return sentence;
}

解析后将中文和英文读取出来。

打印信息

核心代码如下。

try {// 5 利用 Graphics2D 在图片上打印中英文对照语log.info("开始在图像上打印中英文对照语");Graphics2DPoster graphics2DPoster = Graphics2DUtils.drawImage(ImageIO.read(bczInfoImage));graphics2DPoster.setZh(jscbInfo.getCn());Graphics2DUtils.drawZhString(graphics2DPoster);graphics2DPoster.setEn(jscbInfo.getEn());Graphics2DUtils.drawEnString(graphics2DPoster);log.info("在图像上打印中英文对照语成功");// 6. 打印个人的二维码BufferedImage qrcodeImage = ImageIO.read(qcCodeFile);graphics2DPoster.setQrcodeImage(qrcodeImage);Graphics2DUtils.drawQrcode(graphics2DPoster);// 7. 右上角画日期String dateString = Graphics2DUtils.drawDate(graphics2DPoster);// 释放图形上下文,以及它正在使用的任何系统资源。graphics2DPoster.getGraphics2d().dispose();// 8. 最后保存成为文件File posterFile = new File(dailyPosterDirsConfig.getImagesDir()+"/daily_poster_"+dateString+".png");ImageIO.write(graphics2DPoster.getBgImage(), "png",posterFile );log.info("绘制好封面图的海报" + posterFile.getAbsolutePath());
}catch (Exception e){log.error("发生异常:{},{}",e.getMessage(),e);SpringApplication.exit(DailyPosterApplication.applicationContext, () -> 0);return;
}

工具类代码如下。

public class Graphics2DUtils {/*** 留白*/private static final int MARGIN = 25;private static final int SUITABLE_WIDTH = 700;/*** @param bgImage* @return {@code Graphics2DPoster}* @throws IOException*/public static Graphics2DPoster drawImage(BufferedImage bgImage) throws IOException {// 封面图的起始坐标int pic_x = MARGIN, pic_y = MARGIN;Graphics2D graphics2d = bgImage.createGraphics();Graphics2DPoster graphics2dPoster = new Graphics2DPoster(graphics2d);// 海报可容纳的宽度graphics2dPoster.setSuitableWidth(SUITABLE_WIDTH);graphics2dPoster.setBgImage(bgImage);// 记录此时的 y 坐标graphics2dPoster.setCurrentY(pic_y * 40);return graphics2dPoster;}public static Graphics2DPoster drawZhString(Graphics2DPoster graphics2dPoster) throws IOException {// 获取计算机上允许使用的中文字体List<String> fontNames = Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());if (fontNames == null || !fontNames.contains(FontUtil.USE_FONT_NAME)) {throw new RuntimeException("计算机上未安装" + FontUtil.USE_FONT_NAME + "的字体");}// 设置封面图和下方中文之间的距离graphics2dPoster.addCurrentY(30);Graphics2D graphics2d = graphics2dPoster.getGraphics2d();graphics2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);// Font 的构造参数依次是字体名字,字体式样,字体大小Font font = new Font(FontUtil.USE_FONT_NAME, Font.PLAIN, FontUtil.FONT_SIZE);graphics2d.setFont(font);graphics2d.setColor(new Color(255, 255, 255));FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);graphics2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));String zhWrap = FontUtil.makeZhLineFeed(graphics2dPoster.getZh(), metrics, graphics2dPoster.getSuitableWidth());String[] zhWraps = zhWrap.split("\n");for (int i = 0; i < zhWraps.length; i++) {graphics2dPoster.addCurrentY(metrics.getHeight());graphics2d.drawString(zhWraps[i], MARGIN, graphics2dPoster.getCurrentY());}return graphics2dPoster;}public static void drawEnString(Graphics2DPoster graphics2dPoster) throws IOException {// 设置封面图和下方中文之间的距离graphics2dPoster.addCurrentY(20);Graphics2D graphics2d = graphics2dPoster.getGraphics2d();graphics2d.setColor(new Color(255, 255, 255));FontDesignMetrics metrics = FontDesignMetrics.getMetrics(graphics2d.getFont());String enWrap = FontUtil.makeEnLineFeed(graphics2dPoster.getEn(), metrics, graphics2dPoster.getSuitableWidth());String[] enWraps = enWrap.split("\n");for (int i = 0; i < enWraps.length; i++) {graphics2dPoster.addCurrentY(metrics.getHeight());graphics2d.drawString(enWraps[i], MARGIN, graphics2dPoster.getCurrentY());}}/*** 画二维码** @param graphics2dPoster*/public static void drawQrcode(Graphics2DPoster graphics2dPoster) {BufferedImage qrcodeImage = graphics2dPoster.getQrcodeImage();BufferedImage bgImage = graphics2dPoster.getBgImage();// 二维码起始坐标int qrcode_x = bgImage.getWidth() - qrcodeImage.getWidth() - MARGIN;int qrcode_y = bgImage.getHeight() - qrcodeImage.getHeight() - MARGIN;Graphics2D graphics2d = graphics2dPoster.getGraphics2d();graphics2d.drawImage(qrcodeImage, qrcode_x, qrcode_y, qrcodeImage.getWidth(), qrcodeImage.getHeight(), null);}/*** 画日期** @param graphics2dPoster*/public static String drawDate(Graphics2DPoster graphics2dPoster) {BufferedImage bgImage = graphics2dPoster.getBgImage();Date date = new Date();String dateDay = DateUtil.formatDate(date, DateUtil.DD);String dateYearAndMonth = DateUtil.formatDate(date, DateUtil.YYYY_MM);Graphics2D graphics2d = graphics2dPoster.getGraphics2d();graphics2dPoster.setCurrentY(MARGIN * 5);// 先画日期graphics2d.setFont(new Font(FontUtil.USE_FONT_NAME, Font.PLAIN, FontUtil.FONT_SIZE + 30));graphics2d.drawString(dateDay, bgImage.getWidth() - MARGIN * 6, graphics2dPoster.getCurrentY());graphics2dPoster.addCurrentY(66);graphics2d.setFont(new Font(FontUtil.USE_FONT_NAME, Font.PLAIN, FontUtil.FONT_SIZE));//再画年月graphics2d.drawString(dateYearAndMonth, bgImage.getWidth() - MARGIN * 10, graphics2dPoster.getCurrentY());return DateUtil.formatDate(date, DateUtil.YYYYMMDD);}
}

最后就能够实现生成一个有自己的二维码的每日图片。

总结和感谢

示例文件

gitee:

每日海报模块:

https://gitee.com/JunKuangKuang/keenJavaTest-all/tree/master/keenTest-springBoot-parent/daily-poster

参考的开源项目:

https://github.com/qinggee/poster/tree/jinshanciba

更新记录

2023-01-12 发布初版

2024-01-10 编写本文

感谢

感谢现在的好奇,为了能成为更好的自己。

Java生成金山词霸的二维码分享海报

安卓手机抓包

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/625155.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java持久层框架之争:选择最佳方案来提升你的开发效率!

1、前言 在现代软件开发领域&#xff0c;选择适合的持久层框架是至关重要的一步。持久层框架可以帮助我们管理数据访问、数据库连接、事务处理等复杂的数据库操作&#xff0c;从而提升开发效率和代码质量。 然而&#xff0c;在众多的Java持久层框架中&#xff0c;选择最佳方案并…

算法通关村番外篇-LeetCode编程从0到1系列五

大家好我是苏麟 , 今天带来算法通关村番外篇-LeetCode编程从0到1系列五 . 数学 1523. 在区间范围内统计奇数数目 描述 : 给你两个非负整数 low 和 high 。请你返回 low 和 high 之间&#xff08;包括二者&#xff09;奇数的数目。 题目 : LeetCode 1523. 在区间范围内统计奇…

Spring Data JPA 踩过的坑实录

前言 游戏中台一直在使用spring 全家桶&#xff0c; 本文会左右使用Spring Data JPA的坑点记录总结 主要给大家总结介绍了关于使用Spring JPA注意事项及踩过的坑。 案例1&#xff1a; 为什么只调用了 org.springframework.data.repository.CrudRepository#findById(ID id) 却…

孤儿进程与僵尸进程以及僵尸进程的解决

孤儿进程&#xff1a; 定义&#xff1a; 父进程运行结束&#xff0c;但子进程还在运行&#xff08;未运行结束&#xff09;&#xff0c;这样的子进程就称为孤儿进程&#xff08; Orphan Process &#xff09;。 过程&#xff1a; 每当出现一个孤儿进程的时候&#xff0c;内核就…

rtklib读取原始数据是一次读取了一个文件的全部数据

一般来说&#xff0c;rtklib读取观测值文件&#xff08;o文件&#xff09;和导航文件&#xff08;n文件&#xff09;进行解算。 读取文件的时候&#xff0c;并非一次读取一个历元&#xff0c;而是将一个文件所有历元的数据都读取完毕以后&#xff0c;再进行解算。 这看起来是…

《C++大学教程》4.34阶乘

题目&#xff1a; 对一个非负整数n来说&#xff0c;它的阶乘可以写成 n! (读作“n的阶乘”)&#xff0c;其计算公式定义如下&#xff1a; n! n x (n-1) x (n-2)x......x1&#xff08;对于大于1的 n &#xff09; 和 n! 1 ( 对于等于0或者等于1的n ) 例如&#xff0c;5&…

重学Java 6 流程控制语句

我与我&#xff0c;至死不渝 ——24.1.15 模块重点&#xff1a; ①会使用Scanner和Random ②会使用switch以及知道case的穿透性 ③会使用if ④会使用for循环&#xff0c;while循环&#xff0c;嵌套循环 一、键盘录入_Scanner 1.概述&#xff1a;是Java定义好的一个类 2.作用&am…

网络安全等级保护测评规划与设计

笔者单位网络结构日益复杂&#xff0c;应用不断增多&#xff0c;使信息系统面临更多的风险。同时&#xff0c;网络攻防技术发展迅速&#xff0c;攻击的技术门槛随着自动化攻击工具的应用也在不断降低&#xff0c;勒索病毒等未知威胁也开始泛滥。基于此&#xff0c;笔者单位拟进…

一篇文章带你搞懂多线程面试相关的一些问题

目录 1.Callable接口 1.1使用Callable接口来创建线程 1.1相关面试题&#xff1a; 介绍下 Callable 是什么 2.JUC常见的类&#xff08;java.util,concurrent) 2.1ReentrantLock ReentrantLock和sychronized的区别 3.信号量 4.CountDownLatch 5.线程安全的集合类 5.1多线…

yolov7_Obb环境安装

下载obb代码之后&#xff0c;除了安装python和pytorch环境&#xff0c;由于还需要编译nms部分的c代码&#xff0c;因此还需要安装Visual Studio. 这里推荐安装Visual Studio2019版本。 然后在系统环境中配置环境变量 C:\Program Files (x86)\Microsoft Visual Studio\2019\Co…

案例127:基于微信小程序的预约挂号系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

文件按名称分类,批量归类到指定文件夹

我们的生活中充满了各种各样的文件&#xff1a;工作报告、家庭照片、旅行纪念品等&#xff0c;然而文件管理却是一个让人头疼的问题。你是否也曾在寻找某些文件名的重要文件&#xff0c;却因为文件混乱无章的堆放而感到烦躁不安&#xff1f;现在&#xff0c;有了我们【文件批量…

HTML--JavaScript--引入方式

啊哈~~~基础三剑看到第三剑&#xff0c;JavaScript HTML用于控制网页结构 CSS用于控制网页的外观 JavaScript用于控制网页的行为 JavaScript引入方式 引入的三种方式&#xff1a; 外部JavaScript 内部JavaScript 元素事件JavaScript 引入外部JavaScript 一般情况下网页最好…

积极参与建设“一带一路”,川宁生物与微构工场达成战略合作

2024年1月12日&#xff0c;北京微构工场生物技术有限公司&#xff08;以下简称“微构工场”&#xff09;与伊犁川宁生物技术股份有限公司&#xff08;“川宁生物”&#xff09;宣布签订战略合作协议&#xff0c;双方将共同出资设立合资公司&#xff0c;加速生物制造产业化落地&…

Linux操作系统——文件详解

1.文件理解预备知识 首先&#xff0c;当我们在磁盘创建一个空文件时&#xff0c;这个文件会不会占据磁盘空间呢&#xff1f; 答案是当然会占据磁盘空间了&#xff0c;因为文件是空的&#xff0c;仅仅指的是它的内容是空的&#xff0c;但是该文件要有对应的文件名&#xff0c;…

Redis图形界面闪退/错误2系统找不到指定文件/windows无法启动Redis/不是内部或外部命令,也不是可运行的程序

Redis图形界面闪退/错误2系统找不到指定文件/windows无法启动Redis/不是内部或外部命令&#xff0c;也不是可运行的程序 我遇到了以上的问题。 其实&#xff0c;最重要的原因是我打开不了another redis desktop mannager&#xff0c;就是我安装了之后&#xff0c;无法打开它…

【嵌入式学习笔记-02】什么是库文件,静态库的制作和使用,动态库的制作和使用,动态库的动态加载

【嵌入式学习笔记-02】什么是库文件&#xff0c;静态库的制作和使用&#xff0c;动态库的制作和使用&#xff0c;动态库的动态加载 文章目录 什么是库文件&#xff1f;编程模型的发展什么是库文件&#xff1f; 静态库的制作和使用动态库的制作和使用动态库的动态加载 什么是库文…

Docker-01-安装基础命令

Docker-01-安装&基础命令 文章目录 Docker-01-安装&基础命令一、Docker是什么&#xff1f;二、安装Docker①&#xff1a;卸载旧版②&#xff1a;配置Docker的yum库③&#xff1a;安装Docker④&#xff1a;启动和校验⑤&#xff1a;配置镜像加速01&#xff1a;注册阿里云…

SpringBoot知识02

1、快速生成mapper和service &#xff08;1&#xff09;&#xff08;自动生成简单的单表sql&#xff09; &#xff08;2&#xff09;快速生成多表&#xff08;自动生成常量&#xff09; 2、springboot配置swagger&#xff08;路径不用加/api&#xff09; &#xff08;1&#…

Mindspore 公开课 - GPT

GPT Task 在模型 finetune 中&#xff0c;需要根据不同的下游任务来处理输入&#xff0c;主要的下游任务可分为以下四类&#xff1a; 分类&#xff08;Classification&#xff09;&#xff1a;给定一个输入文本&#xff0c;将其分为若干类别中的一类&#xff0c;如情感分类、…