序言
大学毕设做的是 游戏-QQ机器人查询工具,选择的国外super cell的游戏COC(也是因为国内的游戏生态不提供API接口,一般都被鹅厂、猪场"独裁"了,甚至super cell最近上线的荒野行动国服,也被鹅肠进行了掌控)。
选择的QQ作为载体,所以很多大数据量的情况加就需要弃用文字,使用图片。那么使用图片就需要进行图片生成,所以开始在java.awt.* 的包下进行各种踩坑之旅。
都说java处理图片,没有python优秀。但是既然选择了java,就要对他负责到底。
java只是一门语言,优秀与否,还是取决去使用它的人!
- 首先亮出两张java生成后的图片给大家看效果。
这里并不完全都是代码生成的,模板是提前做好的!其目的也是为了减少代码的重复工作!
在开发过程中,使用BufferedImage遇到的问题,解决的和未解决的问题!
1.锯齿效果(已解决)
之前生成的图片,文字放大后完全不能看,被啃了一样,文字小的看了也别扭!
public static void main(String[] args) {
try {
BufferedImage image = new BufferedImage(800, 900, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D)image.createGraphics();
g.setFont(new Font("微软雅黑", Font.BOLD, 100));
g.setColor(Color.white);
g.drawString("文字测试", 60, 200);
ImageIO.write(image, "png", new File("G:\\image\\test2.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
上面是正常情况下写的代码,但是生成的图片,文字和“狗啃了”差不多,于是在百度的了解下,加入代码!
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
完美解决!!!
2.自定义文字颜色and文字字体(已解决)
在生成图片中,java自带的字体、颜色不够我们使用。
颜色的问题,Color类就自带的有使用RGB来进行绘色!
Color color = new Color(红,绿,蓝)
字体的问题,【后期以及全部用java自带的字体】
①首先贴出一段查询自己电脑可以使用那些文字,以及文字使用后效果的代码。直接复制运行即可。
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageFont {
private static String saveFile = "c://fontAll.png";
public static void main(String[] args) {
try {
java.awt.GraphicsEnvironment eq = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] a = eq.getAvailableFontFamilyNames();
BufferedImage image = new BufferedImage(1600, (a.length+2)*30 + 80, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D)image.createGraphics();
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
for (int i = 1; i < a.length+1; i++) {
Font font = new Font(a[i-1],Font.CENTER_BASELINE,25);
g.setFont(font);
g.drawString(a[i-1], 10, 30*i);
g.drawString("1234567890N一", 800, 30*i);
}
ImageIO.write(image, "png", new File(saveFile));
} catch (IOException e) {
e.printStackTrace();
}
}
}
②引入自定义字体
【经过测试,执行速度没有使用java自带的字体执行快】
这里就不引入代码了,我做了简单封装,可能大家并不适用,但是论坛有相关帖子java引入自定义字体的方法。直接食用。展示引入“包图小白体”后的效果图:
3.文字居中 自动换行的处理。(未完美处理)
g.drawString(内容,X, y);
- 因为文字是根据文字内容,XY坐标从做往右进行排列,并不会进行居中,和自动换行操作
- 自认为,换行处理没有处理的完美。 代码冗余,使用的substring分割的字符串。
- 文字换行处理方式:
分割字符串 --> 分割后循环遍历,增加y轴高度。
这里有个很严重的问题,就是 汉字 和 数字字母符号 长度不同,对于处理文字+数字的方式,界面极不友好美观,所以在处理字符串的时候将所有字符进行的全角处理!当然字体也会影响文字的大小!
- 文本内容居中 代码如下
Font font=new Font("微软雅黑",Font.PLAIN,50);
FontMetrics fontMetrics = g.getFontMetrics(Font);
int textWidth = fontMetrics .stringWidth(文本内容);
int X = (图片宽度 - textWidth) / 2;
是否居中一目了然,如下生成的文字居中图片
4.特殊文字处理,只显示方框(部分处理)
这个坑,确实难填。因为coc是一款全球游戏,覆盖了各国语言,所以很多种语言,或者符号,drawString 都是不认可的,所以只能在填入的text文本内容之前,对text进行处理了,这点没啥好说的,转换字符集。百度各种办法,但是用到对于特殊符号,最好的解决办法还是使用“Menlo”的的字体!!!
Font font = new Font("Menlo", Font.BOLD, 120)
5.图片处理问题--压缩、水印、旋转、拼接
这里推荐Google的java开源 thumbnailator !!!
BufferedImage - Graphics2D 图片处理 文字居中换行util
/***
*
* @param g Graphics2D
* @param font Font
* @param text 文本内容
* @param x 起始点X轴坐标
* @param y 起始点Y轴坐标
* @param maxWidth 文字最大长度
*/
public static void content(Graphics2D g , Font font , String text , int x , int y , int maxWidth) {
FontMetrics fontMetrics = g.getFontMetrics(font);
int textWidth = fontMetrics.stringWidth(text);
int X = (maxWidth - textWidth) / 2;
g.drawString(text,x+X,y);
}
BufferedImage - Graphics2D 图片处理 文字自动换行util
/***
*
* @param g Graphics2D
* @param font Font
* @param text 文本内容
* @param x 起始点X轴坐标
* @param y 起始点Y轴坐标
* @param maxWidth 文字最大长度
*/
public static void drawString(Graphics2D g , Font font , String text , int x , int y , int maxWidth) {
JLabel label = new JLabel(text);
label.setFont(font);
FontMetrics metrics = label.getFontMetrics(label.getFont());
int textH = metrics.getHeight();
int textW = metrics.stringWidth(label.getText()); //字符串的宽
String tempText = text;
while(textW > maxWidth) {
int n = textW / maxWidth;
int subPos = tempText.length() / n;
String drawText = tempText.substring(0 , subPos);
int subTxtW = metrics.stringWidth(drawText);
while(subTxtW > maxWidth) {
subPos--;
drawText = tempText.substring(0 , subPos);
subTxtW = metrics.stringWidth(drawText);
}
g.drawString(drawText , x , y);
y += textH;
textW = textW - subTxtW;
tempText = tempText.substring(subPos);
}
g.drawString(tempText , x , y);
}
1.关于获取网络图片
网络图片,通过HTTP获取图片,然后进行处理,或者拼接!
如下,httpsGet 方法,可获取HTTPS 需要进行SSL校验的请求,并获取返回的BufferedImage
HTTP获取的方法,网络有很多。其中只需要通过InputStream 流获取,然后通过ImageIO.read(InputStream);的方式将内容转换成BufferedImage供我们进行操作
- 如下提供一个发送HTTPS请求的工具类,需要HttpClient工具 ,如下导入maven
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.7</version>
</dependency>
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpUtils;
import org.apache.commons.*;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Https {
private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);
/**
* 定义类型,用于获取不同类型的httpclient
*/
enum CLIENT_TYPE {
HTTP, HTTPS
}
/**
* https post请求
*
* @param url 请求地址
* @param headerMap 请求头信息
* @param contentMap 请求体信息
* @return 结果返回
*/
public static String httpsPost(String url, Map<String, String> headerMap, Map<String, String> contentMap) {
return httpsPost(url, headerMap, contentMap, "UTF-8");
}
/**
* http post请求
*
* @param url 请求地址
* @param headerMap 请求头信息
* @param contentMap 请求体信息
* @return 结果返回
*/
public static String httpPost(String url, Map<String, String> headerMap, Map<String, String> contentMap) {
return httpPost(url, headerMap, contentMap, "UTF-8");
}
/**
* https get请求
*
* @param url 请求地址
* @param paramMap 请求参数
* @return 结果返回
*/
public static BufferedImage httpsGet(String url, Map<String, String> paramMap) {
return httpsGet(url, paramMap, "UTF-8");
}
/**
* https get请求
*
* @param url 请求地址
* @param paramMap 请求参数
* @return 结果返回
*/
public static String httpsGetTxt(String url, Map<String, String> paramMap) {
return httpsGetTxt(url, paramMap, "UTF-8");
}
/**
* http get请求
*
* @param url 请求地址
* @param paramMap 请求参数
* @return 结果返回
*/
public static BufferedImage httpGet(String url, Map<String, String> paramMap) {
return httpGet(url, paramMap, "UTF-8");
}
/**
* @param url 请求地址
* @param headerMap 请求头信息
* @param contentMap 请求体信息
* @param charset 编码类型
* @return 结果返回
*/
public static String httpsPost(String url, Map<String, String> headerMap, Map<String, String> contentMap, String charset) {
return post(url, headerMap, contentMap, charset, CLIENT_TYPE.HTTPS);
}
/**
* @param url 请求地址
* @param headerMap 请求头信息
* @param contentMap 请求体信息
* @param charset 编码类型
* @return 结果返回
*/
public static String httpPost(String url, Map<String, String> headerMap, Map<String, String> contentMap, String charset) {
return post(url, headerMap, contentMap, charset, CLIENT_TYPE.HTTP);
}
/**
* @param url 请求地址
* @param paramMap 请求参数
* @param charset 编码类型
* @return 结果返回
*/
public static BufferedImage httpsGet(String url, Map<String, String> paramMap, String charset) {
return get(url, paramMap, charset, CLIENT_TYPE.HTTPS);
}
/**
* @param url 请求地址
* @param paramMap 请求参数
* @param charset 编码类型
* @return 结果返回
*/
public static String httpsGetTxt(String url, Map<String, String> paramMap, String charset) {
return getTxt(url, paramMap, charset, CLIENT_TYPE.HTTPS);
}
/**
* @param url 请求地址
* @param paramMap 请求参数
* @param charset 编码类型
* @return 结果返回
*/
public static BufferedImage httpGet(String url, Map<String, String> paramMap, String charset) {
return get(url, paramMap, charset, CLIENT_TYPE.HTTP);
}
/**
* post 请求的实际方法
*
* @param url 请求地址
* @param headerMap 请求头信息
* @param contentMap 请求体信息
* @param charset 编码类型
* @param type 协议类型
* @return 结果返回
*/
private static String post(String url, Map<String, String> headerMap, Map<String, String> contentMap, String charset, CLIENT_TYPE type) {
String result = null;
HttpClient httpClient = null;
try {
HttpPost post = new HttpPost(url);
if (MapUtils.isNotEmpty(headerMap)) {// 设置请求头
for (Map.Entry<String, String> entry : headerMap.entrySet()) {
post.addHeader(entry.getKey(), entry.getValue());
}
}
if (MapUtils.isNotEmpty(contentMap)) {// 设置请求体
List<NameValuePair> content = getNameValuePairList(contentMap);
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(content, charset);
post.setEntity(entity);
}
httpClient = getClient(type);//这里是重点,根据不同协议获取不同类型的client端
HttpResponse response = httpClient.execute(post);//发送请求并接收返回数据
if (response != null) {
HttpEntity resEntity = response.getEntity();
if (resEntity != null) {
result = EntityUtils.toString(resEntity, charset);
}
}
return result;
} catch (Exception ex) {
throw new RuntimeException("请求:" + url + " 异常:" + ex.getMessage());
} finally {
try {
if (null != httpClient && null != httpClient.getConnectionManager()) {
httpClient.getConnectionManager().shutdown();
}
} catch (Exception e) {
logger.error("请求:" + url + " 流关闭异常或者httpclient关闭异常");
}
}
}
/**
* get 请求的实际方法
*
* @param url 请求地址
* @param paramMap 请求参数
* @param charset 编码类型
* @param type 协议类型
* @return 结果返回
*/
private static BufferedImage get(String url, Map<String, String> paramMap, String charset, CLIENT_TYPE type) {
HttpClient httpClient = null;
try {
if (MapUtils.isNotEmpty(paramMap)) {// 拼接参数
// 设置请求体
List<NameValuePair> content = getNameValuePairList(paramMap);
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(content, charset);
String params = EntityUtils.toString(entity);
url = url + "?" + params;
}
HttpGet get = new HttpGet(url);
get.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36");
get.addHeader("authorization", "Bearer"+BackEndHttpRequest.API);
httpClient = getClient(type);
RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) //连接超时时间
.setSocketTimeout(1000*10) //数据传输的超时时间
.build();
get.setConfig(config);
HttpResponse response = httpClient.execute(get); //发送请求并接收返回数据
if (response != null) {
HttpEntity resEntity = response.getEntity();
String value = resEntity.getContentType().getValue();
System.out.println("返回图片为"+value);
InputStream content = resEntity.getContent();
BufferedImage image = ImageIO.read(content);
return image;
}
return null;
} catch (Exception ex) {
throw new RuntimeException("请求:" + url + " 异常:" + ex.getMessage());
} finally {
try {
} catch (Exception e) {
logger.error("请求:" + url + " 流关闭异常或者httpclient关闭异常");
}
}
}
/**
* get 请求的实际方法
*
* @param url 请求地址
* @param paramMap 请求参数
* @param charset 编码类型
* @param type 协议类型
* @return 结果返回
*/
private static String getTxt(String url, Map<String, String> paramMap, String charset, CLIENT_TYPE type) {
String result = "";
HttpClient httpClient = null;
try {
if (MapUtils.isNotEmpty(paramMap)) {// 拼接参数
// 设置请求体
List<NameValuePair> content = getNameValuePairList(paramMap);
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(content, charset);
String params = EntityUtils.toString(entity);
url = url + "?" + params;
}
HttpGet get = new HttpGet(url);
get.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36");
get.addHeader("authorization", "Bearer"+BackEndHttpRequest.API);
httpClient = getClient(type);
RequestConfig config = RequestConfig.custom().setConnectTimeout(1000) //连接超时时间
.setSocketTimeout(1000*10) //数据传输的超时时间
.build();
get.setConfig(config);
HttpResponse response = httpClient.execute(get); //发送请求并接收返回数据
if (response != null) {
HttpEntity resEntity = response.getEntity();
if (resEntity != null) {
result = EntityUtils.toString(resEntity, charset);
}
return result;
}
return null;
} catch (Exception ex) {
throw new RuntimeException("请求:" + url + " 异常:" + ex.getMessage());
} finally {
try {
} catch (Exception e) {
logger.error("请求:" + url + " 流关闭异常或者httpclient关闭异常");
}
}
}
private static List<NameValuePair> getNameValuePairList(Map<String, String> paramMap) {
List<NameValuePair> content = null;
if (MapUtils.isNotEmpty(paramMap)) {
// 设置请求体
content = new ArrayList<NameValuePair>();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
content.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
}
return content;
}
/**
* 根据指定类型返回http、https类型的客户端
*
* @param type 类型
* @return 客户端
* @throws Exception 异常信息
*/
private static DefaultHttpClient getClient(CLIENT_TYPE type) throws Exception {
if (type == CLIENT_TYPE.HTTP) {//http类型
return new DefaultHttpClient();
} else if (type == CLIENT_TYPE.HTTPS) {//https类型
return new SSLClient();
} else {
throw new RuntimeException("未知协议类型,请重新指定");
}
}
/**
* 自定义SSL client
*/
static class SSLClient extends DefaultHttpClient {
public SSLClient() throws Exception {
super();
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
//传输协议需要根据自己的判断
//SSLContext ctx = SSLContext.getInstance("TLSv1.2");
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = this.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", 443, ssf));
}
}
}
评论区