From 51efdf5191bc7ecd4efdd22a46139a45af66b7a5 Mon Sep 17 00:00:00 2001
From: zhongrj <646384940@qq.com>
Date: Wed, 20 Mar 2024 21:40:02 +0800
Subject: [PATCH] 集成微信图片安全检测
---
src/main/java/org/springblade/common/utils/WeiXinSecurityUtil.java | 321 ++++++++++++++++
src/main/java/org/springblade/modules/resource/utils/ImageUtil.java | 171 ++++++++
src/main/java/org/springblade/modules/resource/endpoint/OssEndpoint.java | 48 ++
src/main/java/org/springblade/common/utils/ImageUtils.java | 39 +
pom.xml | 7
src/main/java/org/springblade/common/utils/HttpClientUtils.java | 596 +++++++++++++++++++++++++++++
6 files changed, 1,177 insertions(+), 5 deletions(-)
diff --git a/pom.xml b/pom.xml
index b3ea7f4..c81e921 100644
--- a/pom.xml
+++ b/pom.xml
@@ -257,6 +257,13 @@
<artifactId>tess4j</artifactId>
<version>4.5.4</version>
</dependency>
+ <!--压缩图片-->
+ <dependency>
+ <groupId>net.coobird</groupId>
+ <artifactId>thumbnailator</artifactId>
+ <version>0.4.8</version>
+ </dependency>
+
</dependencies>
diff --git a/src/main/java/org/springblade/common/utils/HttpClientUtils.java b/src/main/java/org/springblade/common/utils/HttpClientUtils.java
new file mode 100644
index 0000000..e75f430
--- /dev/null
+++ b/src/main/java/org/springblade/common/utils/HttpClientUtils.java
@@ -0,0 +1,596 @@
+package org.springblade.common.utils;
+
+import com.alibaba.fastjson.JSON;
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.*;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.util.EntityUtils;
+import sun.misc.BASE64Encoder;
+
+import javax.net.ssl.SSLContext;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.*;
+
+
+public class HttpClientUtils {
+ /**
+ * 执行有参GET请求
+ *
+ * @param url
+ * @param params
+ * @return
+ */
+ public static String doGet(String url, Map<String, String> params) {
+
+//获取httpclient客户端
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+
+ String resultString = "";
+
+ CloseableHttpResponse response = null;
+
+ try {
+ URIBuilder builder = new URIBuilder(url);
+
+ if (null != params) {
+ for (String key : params.keySet()) {
+ builder.setParameter(key, params.get(key));
+ }
+ }
+
+ HttpGet get = new HttpGet(builder.build());
+
+ response = httpclient.execute(get);
+
+ System.out.println(response.getStatusLine());
+
+ if (200 == response.getStatusLine().getStatusCode()) {
+ HttpEntity entity = response.getEntity();
+ resultString = EntityUtils.toString(entity, "utf-8");
+ }
+
+ } catch (Exception e) {
+
+ e.printStackTrace();
+ } finally {
+ if (null != response) {
+ try {
+ response.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (null != httpclient) {
+ try {
+ httpclient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return resultString;
+ }
+
+ /**
+ * 执行有参GET请求,带请求头
+ *
+ * @param url 请求url
+ * @param params 参数
+ * @param key 请求头Key
+ * @param secretKey 秘钥
+ * @return
+ */
+ public static String doGetHeader(String url, String key, String secretKey,Map<String, String> params) {
+
+//获取httpclient客户端
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+
+ String resultString = "";
+
+ CloseableHttpResponse response = null;
+
+ try {
+ URIBuilder builder = new URIBuilder(url);
+
+ if (null != params) {
+ for (String keys : params.keySet()) {
+ builder.addParameter(keys,params.get(keys));
+//builder.setParameter(keys, params.get(keys));
+ }
+ }
+
+ HttpGet httpGet = new HttpGet(builder.build());
+
+//设置请求头
+ httpGet.addHeader(key,secretKey);
+// 传输的类型
+ httpGet.addHeader("Content-Type", "application/x-www-form-urlencoded");
+//执行Http请求调用
+ response = httpclient.execute(httpGet);
+//判断是否请求成功返回
+ if (200 == response.getStatusLine().getStatusCode()) {
+ HttpEntity entity = response.getEntity();
+ resultString = EntityUtils.toString(entity, "utf-8");
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (null != response) {
+ try {
+ response.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (null != httpclient) {
+ try {
+ httpclient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return resultString;
+ }
+
+ /**
+ * 执行有参GET请求,带请求头,接收图片流
+ *
+ * @param url 请求url
+ * @param params 参数
+ * @param key 请求头Key
+ * @param secretKey 秘钥
+ * @return
+ */
+ public static String doGetHeaderPictureBase64(String url, String key, String secretKey,Map<String, String> params) {
+
+//获取httpclient客户端
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ String resultString = "";
+ CloseableHttpResponse response = null;
+ try {
+ URIBuilder builder = new URIBuilder(url);
+ if (null != params) {
+ for (String keys : params.keySet()) {
+ builder.addParameter(keys,params.get(keys));
+ }
+ }
+ HttpGet httpGet = new HttpGet(builder.build());
+//设置请求头
+ httpGet.addHeader(key,secretKey);
+// 传输的类型
+ httpGet.addHeader("Content-Type", "application/x-www-form-urlencoded");
+//执行Http请求调用
+ response = httpclient.execute(httpGet);
+// 将返回的图片或者文件转化成字节数组的形式
+ byte[] data = EntityUtils.toByteArray(response.getEntity());
+ BASE64Encoder encoder = new BASE64Encoder();
+//String imageBase64 = "data:image/png;base64," + encoder.encodeBuffer(data).trim();
+ return encoder.encodeBuffer(data).trim().replaceAll("\n", "").replaceAll("\r", "").replaceAll(" ", "");//删除 \r\n
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (null != response) {
+ try {
+ response.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (null != httpclient) {
+ try {
+ httpclient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return resultString;
+ }
+
+ /**
+ * 执行无参GET请求
+ *
+ * @param url
+ * @return
+ */
+ public static String doGet(String url) {
+ return doGet(url, null);
+ }
+
+ /**
+ * 执行有参POST请求
+ *
+ * @param url
+ * @param params
+ * @return
+ */
+ public static String doPost(String url, Map<String, String> params) {
+/**
+ * 在4.0及以上httpclient版本中,post需要指定重定向的策略,如果不指定则按默认的重定向策略。
+ *
+ * 获取httpclient客户端
+ */
+ CloseableHttpClient httpclient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();
+
+ String resultString = "";
+
+ CloseableHttpResponse response = null;
+
+ try {
+
+
+ HttpPost post = new HttpPost(url);
+
+ List<NameValuePair> paramaters = new ArrayList<>();
+
+ if (null != params) {
+ for (String key : params.keySet()) {
+ paramaters.add(new BasicNameValuePair(key, params.get(key)));
+ }
+// 构造一个form表单式的实体
+ UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(paramaters);
+// 将请求实体设置到httpPost对象中
+ post.setEntity(formEntity);
+ }
+
+
+/**
+ * HTTP/1.1 403 Forbidden
+ * 原因:
+ * 有些网站,设置了反爬虫机制
+ * 解决的办法:
+ * 设置请求头,伪装浏览器
+ */
+ post.addHeader("user-agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
+
+ response = httpclient.execute(post);
+
+ System.out.println(response.getStatusLine());
+
+ if (200 == response.getStatusLine().getStatusCode()) {
+ HttpEntity entity = response.getEntity();
+ resultString = EntityUtils.toString(entity, "utf-8");
+ }
+
+ } catch (Exception e) {
+
+ e.printStackTrace();
+ } finally {
+ if (null != response) {
+ try {
+ response.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (null != httpclient) {
+ try {
+ httpclient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return resultString;
+ }
+
+ public static String doPost(String url) {
+ return doPost(url, null);
+ }
+
+
+ public static void main(String[] args) {
+
+ Map<String, String> params = new HashMap<>();
+ params.put("scope", "project");
+ params.put("q", "数据库");
+//
+// System.out.println(doGet("http://www.baidu.com/s",params));
+
+ /**
+ * 有一部分网站,禁止爬虫技术访问网站。
+ *
+ * 解决方案:
+ * 伪装浏览器
+ * post.addHeader("user-agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
+ */
+ System.out.println(doPost("http://www.oschina.net/search", params));
+ }
+
+ /**
+ * post 请求 header 带 秘钥
+ * @param url
+ * @param appKey
+ * @param appKeyValue
+ * @param map
+ * @return
+ */
+ public static String httpPost(String url, String appKey, String appKeyValue, Map<String, String> map) {
+// 返回body
+ String body = null;
+// 获取连接客户端工具
+ CloseableHttpClient httpClient = HttpClients.createDefault();
+ CloseableHttpResponse httpResponse = null;
+// 2、创建一个HttpPost请求
+ HttpPost post = new HttpPost(url);
+// 5、设置header信息
+ /**header中通用属性*/
+// post.setHeader("Accept", "*/*");
+// post.setHeader("Accept-Encoding", "gzip, deflate");
+// post.setHeader("Cache-Control", "no-cache");
+// post.setHeader("Connection", "keep-alive");
+ post.setHeader("Content-Type", "application/json;charset=UTF-8");
+/**业务参数*/
+ post.setHeader(appKey, appKeyValue);
+// 设置参数
+ if (map != null) {
+ try {
+ StringEntity entity1 = new StringEntity(JSON.toJSONString(map), "UTF-8");
+ entity1.setContentEncoding("UTF-8");
+ entity1.setContentType("json/form-data");
+ post.setEntity(entity1);
+
+// 7、执行post请求操作,并拿到结果
+ httpResponse = httpClient.execute(post);
+// 获取结果实体
+ HttpEntity entity = httpResponse.getEntity();
+ if (entity != null) {
+// 按指定编码转换结果实体为String类型
+ body = EntityUtils.toString(entity, "UTF-8");
+ }
+ try {
+ httpResponse.close();
+ httpClient.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return body;
+ }
+
+ public static SSLContext createIgnoreVerifySSL() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
+
+ SSLContext sc = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
+
+ public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
+ return true;
+ }
+
+ }).build();
+
+ return sc;
+ }
+
+ //适用于post请求并传送form-data数据(同样适用于post的Raw类型的application-json格式)
+ public static String postParams(String url, String appKey, Map<String, String> map) {
+ SSLContext sslcontext = null;
+ try {
+ sslcontext = createIgnoreVerifySSL();
+ } catch (KeyManagementException e) {
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ }
+// 设置协议http和https对应的处理socket链接工厂的对象
+ Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
+ .register("http", PlainConnectionSocketFactory.INSTANCE)
+ .register("https", new SSLConnectionSocketFactory(sslcontext))
+ .build();
+ PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
+
+//创建自定义的httpclient对象
+ CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).build();
+ HttpPost post = new HttpPost(url);
+ post.setHeader("appKey", appKey);
+ CloseableHttpResponse res = null;
+ try {
+ List<NameValuePair> nvps = new ArrayList<NameValuePair>();
+ Set<String> keySet = map.keySet();
+ for (String key : keySet) {
+ nvps.add(new BasicNameValuePair(key, map.get(key)));
+ }
+ post.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
+ res = client.execute(post);
+ HttpEntity entity = res.getEntity();
+ return EntityUtils.toString(entity, "utf-8");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ res.close();
+ client.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return "";
+ }
+
+
+
+ //适用于post请求并传送form-data数据(同样适用于post的Raw类型的application-json格式)
+ public static String postParams(String url, Map<String, Object> map) {
+ SSLContext sslcontext = null;
+ try {
+ sslcontext = createIgnoreVerifySSL();
+ } catch (KeyManagementException e) {
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ }
+// 设置协议http和https对应的处理socket链接工厂的对象
+ Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
+ .register("http", PlainConnectionSocketFactory.INSTANCE)
+ .register("https", new SSLConnectionSocketFactory(sslcontext))
+ .build();
+ PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
+
+//创建自定义的httpclient对象
+ CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).build();
+ HttpPost post = new HttpPost(url);
+// post.setHeader(appKey, appKeyValue);
+ CloseableHttpResponse res = null;
+ try {
+ List<NameValuePair> nvps = new ArrayList<NameValuePair>();
+ Set<String> keySet = map.keySet();
+ for (String key : keySet) {
+ nvps.add(new BasicNameValuePair(key, map.get(key).toString()));
+ }
+ post.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
+ res = client.execute(post);
+ HttpEntity entity = res.getEntity();
+ return EntityUtils.toString(entity, "utf-8");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ res.close();
+ client.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return "";
+ }
+
+ /**
+ * 向指定 URL 发送POST方法的请求
+ *
+ * @param url 发送请求的 URL
+ * @param params 请求的参数集合
+ * @return 远程资源的响应结果
+ */
+ @SuppressWarnings("unused")
+ public static String sendPost(String url, Map<String, String> params) {
+ OutputStreamWriter out = null;
+ BufferedReader in = null;
+ StringBuilder result = new StringBuilder();
+ try {
+ URL realUrl = new URL(url);
+ HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
+// 发送POST请求必须设置如下两行
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+// POST方法
+ conn.setRequestMethod("POST");
+// 设置通用的请求属性
+ conn.setRequestProperty("accept", "*/*");
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+ conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ conn.connect();
+// 获取URLConnection对象对应的输出流
+ out = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
+// 发送请求参数
+ if (params != null) {
+ StringBuilder param = new StringBuilder();
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ if (param.length() > 0) {
+ param.append("&");
+ }
+ param.append(entry.getKey());
+ param.append("=");
+ param.append(entry.getValue());
+// System.out.println(entry.getKey()+":"+entry.getValue());
+ }
+// System.out.println("param:"+param.toString());
+ out.write(param.toString());
+ }
+// flush输出流的缓冲
+ out.flush();
+// 定义BufferedReader输入流来读取URL的响应
+ in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
+ String line;
+ while ((line = in.readLine()) != null) {
+ result.append(line);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+// 使用finally块来关闭输出流、输入流
+ finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return result.toString();
+ }
+
+
+ /**
+ * json body
+ * @param url
+ * @param json
+ * @return
+ * @throws IOException
+ */
+ public static String httpPostWithjson(String url, String json) throws IOException {
+ String result = "";
+ HttpPost httpPost = new HttpPost(url);
+ CloseableHttpClient httpClient = HttpClients.createDefault();
+ try {
+ BasicResponseHandler handler = new BasicResponseHandler();
+ //解决中文乱码问题
+ StringEntity entity = new StringEntity(json, "utf-8");
+ entity.setContentEncoding("UTF-8");
+ entity.setContentType("application/json");
+ httpPost.setEntity(entity);
+ result = httpClient.execute(httpPost, handler);
+ return result;
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ httpClient.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return result;
+ }
+}
+
+
diff --git a/src/main/java/org/springblade/common/utils/ImageUtils.java b/src/main/java/org/springblade/common/utils/ImageUtils.java
index 0fcf2c4..a0af0b6 100644
--- a/src/main/java/org/springblade/common/utils/ImageUtils.java
+++ b/src/main/java/org/springblade/common/utils/ImageUtils.java
@@ -8,6 +8,7 @@
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
@@ -16,6 +17,44 @@
public class ImageUtils {
+ /**
+ * 几种常见的图片格式
+ */
+ public static String IMAGE_TYPE_GIF = "gif";// 图形交换格式
+ public static String IMAGE_TYPE_JPG = "jpg";// 联合照片专家组
+ public static String IMAGE_TYPE_JPEG = "jpeg";// 联合照片专家组
+ public static String IMAGE_TYPE_BMP = "bmp";// 英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式
+ public static String IMAGE_TYPE_PNG = "png";// 可移植网络图形
+ public static String IMAGE_TYPE_PSD = "psd";// Photoshop的专用格式Photoshop
+
+
+ /**
+ * 程序入口:用于测试
+ * @param args
+ */
+ public static void main(String[] args) {
+ // -图像类型转换:
+ ImageUtils.convert("D:/IMG_20180811_222034.png", "jpg", "D:/test.jpg");//测试OK
+ }
+
+ /**
+ * 图像类型转换:GIF->JPG、GIF->PNG、PNG->JPG、PNG->GIF(X)、BMP->PNG
+ * @param srcImageFile 源图像地址
+ * @param formatName 包含格式非正式名称的 String:如JPG、JPEG、GIF等
+ * @param destImageFile 目标图像地址
+ */
+ public static void convert(String srcImageFile, String formatName, String destImageFile) {
+ try {
+ File f = new File(srcImageFile);
+ f.canRead();
+ f.canWrite();
+ BufferedImage src = ImageIO.read(f);
+ ImageIO.write(src, formatName, new File(destImageFile));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
public static String getBase64ByImgUrl(String url){
diff --git a/src/main/java/org/springblade/common/utils/WeiXinSecurityUtil.java b/src/main/java/org/springblade/common/utils/WeiXinSecurityUtil.java
new file mode 100644
index 0000000..675d02a
--- /dev/null
+++ b/src/main/java/org/springblade/common/utils/WeiXinSecurityUtil.java
@@ -0,0 +1,321 @@
+package org.springblade.common.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.io.FileUtils;
+import java.awt.image.BufferedImage;
+import javax.imageio.ImageIO;
+import net.coobird.thumbnailator.Thumbnails;
+
+/**
+ * 微信安全检验工具类
+ */
+public class WeiXinSecurityUtil {
+ private static Integer IMG_WIDTH = 750;
+ private static Integer IMG_HEIGHT = 1334;
+ /**
+ * 图片检测接口
+ */
+ private static String IMG_SEC_URL = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=";
+
+ //获取wxAccessToken
+ private static String WX_GET_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token";
+
+ private static String WX_APP_ID = "wx41aa8a5d2e565a05";
+ private static String WX_SECRET = "e0e9218ac368cd4ca620ff4c7030f468";
+
+ /**
+ * 恶意图片过滤-校验图片是否含有违法违规内容
+ * MultipartFile multipartFile, String accessToken
+ * @param multipartFile
+ * @return
+ */
+ public static String checkImg(MultipartFile multipartFile){
+ String result = "";
+ //压缩图片
+ InputStream inputStream = compressImage(multipartFile);
+ // 校验api url
+ String url = IMG_SEC_URL + getWxAccessToken();
+ // 获得Http客户端(可以理解为:你得先有一个浏览器;注意:实际上HttpClient与浏览器是不一样的
+ HttpClient httpclient = HttpClients.createDefault();
+ //创建一个post请求
+ HttpPost request = new HttpPost(url);
+ //设置响应头 ( application/octet-stream:二进制流,不知道下载文件类型)
+ request.addHeader("Content-Type", "application/octet-stream");
+ try {
+ //创建一个byte数组,和输入的文件的大小一样
+ byte[] byt = new byte[inputStream.available()];
+ //从输入流中读取全部,并将其存储在缓冲区数组byt 中。
+ inputStream.read(byt);
+ //定制提交内容
+ request.setEntity(new ByteArrayEntity(byt, ContentType.create("image/jpg")));
+ //由客户端执行(发送)请求,执行校验
+ HttpResponse response = httpclient.execute(request);
+ // 从响应模型中获取响应实体
+ HttpEntity entity = response.getEntity();
+ result = EntityUtils.toString(entity, "UTF-8");// 转成string
+ //打印校验结果
+ System.out.println("result:" + result);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if(inputStream != null){
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ // 响应返回结果
+ return result;
+ }
+
+
+
+ /**
+ * 图片过滤检测
+ * @param file 图片文件
+ * @return
+ */
+ public String checkPic(MultipartFile file) {
+ String access_token = "";
+ String result = "";
+ //自己写一个定时任务或其他方式 获取AccessToken
+ try {
+ access_token = getWxAccessToken();
+ String url = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=" + access_token;
+ result = uploadFile(url, file);
+ System.out.println("图片检测结果 = " + result);
+ } catch (Exception e) {
+ System.out.println("----------------调用腾讯内容过滤系统出错------------------" + e.getMessage());
+ }
+ // 返回
+ return result;
+ }
+
+ /**
+ * 上传二进制文件
+ * @param graphurl 接口地址
+ * @param file 图片文件
+ * @return
+ */
+ public static String uploadFile(String graphurl,MultipartFile file) {
+ String line = null;//接口返回的结果
+ try {
+ // 换行符
+ final String newLine = "\r\n";
+ final String boundaryPrefix = "--";
+ // 定义数据分隔线
+ String BOUNDARY = "========7d4a6d158c9";
+ // 服务器的域名
+ URL url = new URL(graphurl);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ // 设置为POST情
+ conn.setRequestMethod("POST");
+ // 发送POST请求必须设置如下两行
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ conn.setUseCaches(false);
+ // 设置请求头参数
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("Charsert", "UTF-8");
+ conn.setRequestProperty("Content-Type","multipart/form-data; boundary=" + BOUNDARY);
+ conn.setRequestProperty("User-Agent","Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1");
+ OutputStream out = new DataOutputStream(conn.getOutputStream());
+
+ // 上传文件
+ StringBuilder sb = new StringBuilder();
+ sb.append(boundaryPrefix);
+ sb.append(BOUNDARY);
+ sb.append(newLine);
+ // 文件参数,photo参数名可以随意修改
+ sb.append("Content-Disposition: form-data;name=\"image\";filename=\""
+ + "https://api.weixin.qq.com" + "\"" + newLine);
+ sb.append("Content-Type:application/octet-stream");
+ // 参数头设置完以后需要两个换行,然后才是参数内容
+ sb.append(newLine);
+ sb.append(newLine);
+
+ // 将参数头的数据写入到输出流中
+ out.write(sb.toString().getBytes());
+
+ // 读取文件数据
+ out.write(file.getBytes());
+ // 最后添加换行
+ out.write(newLine.getBytes());
+
+ // 定义最后数据分隔线,即--加上BOUNDARY再加上--。
+ byte[] end_data = (newLine + boundaryPrefix + BOUNDARY
+ + boundaryPrefix + newLine).getBytes();
+ // 写上结尾标识
+ out.write(end_data);
+ out.flush();
+ out.close();
+ // 定义BufferedReader输入流来读取URL的响应
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ conn.getInputStream()));
+ while ((line = reader.readLine()) != null) {
+ return line;
+ }
+ } catch (Exception e) {
+ System.out.println("发送POST请求出现异常!" + e);
+ }
+ return line;
+ }
+
+ /**
+ * 上传二进制文件
+ * @param apiurl 接口地址
+ * @param file 图片文件
+ * @return
+ */
+ public static String uploadFile(String apiurl, byte[] file) {
+ //接口返回的结果
+ String line = null;
+ try {
+ // 换行符
+ final String newLine = "\r\n";
+ final String boundaryPrefix = "--";
+ // 定义数据分隔线
+ String BOUNDARY = "========7d4a6d158c9";
+ // 服务器的域名
+ URL url = new URL(apiurl);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ // 设置为POST情
+ conn.setRequestMethod("POST");
+ // 发送POST请求必须设置如下两行
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ conn.setUseCaches(false);
+ // 设置请求头参数
+ conn.setRequestProperty("connection", "Keep-Alive");
+ conn.setRequestProperty("Charsert", "UTF-8");
+ conn.setRequestProperty("Content-Type","multipart/form-data; boundary=" + BOUNDARY);
+ conn.setRequestProperty("User-Agent","Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/7.0.15(0x17000f31) NetType/WIFI Language/zh_CN");
+ OutputStream out = new DataOutputStream(conn.getOutputStream());
+
+ // 上传文件
+ StringBuilder sb = new StringBuilder();
+ sb.append(boundaryPrefix);
+ sb.append(BOUNDARY);
+ sb.append(newLine);
+ // 文件参数,photo参数名可以随意修改
+ sb.append("Content-Disposition: form-data;name=\"image\";filename=\""
+ + "https://api.weixin.qq.com" + "\"" + newLine);
+ sb.append("Content-Type:application/octet-stream");
+ // 参数头设置完以后需要两个换行,然后才是参数内容
+ sb.append(newLine);
+ sb.append(newLine);
+
+ // 将参数头的数据写入到输出流中
+ out.write(sb.toString().getBytes());
+
+ // 读取文件数据
+ out.write(file);
+ // 最后添加换行
+ out.write(newLine.getBytes());
+
+ // 定义最后数据分隔线,即--加上BOUNDARY再加上--。
+ byte[] end_data = (newLine + boundaryPrefix + BOUNDARY
+ + boundaryPrefix + newLine).getBytes();
+ // 写上结尾标识
+ out.write(end_data);
+ out.flush();
+ out.close();
+ // 定义BufferedReader输入流来读取URL的响应
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ conn.getInputStream()));
+ while ((line = reader.readLine()) != null) {
+ return line;
+ }
+ } catch (Exception e) {
+ System.out.println("发送POST请求出现异常!" + e);
+ }
+ return line;
+ }
+
+ /**
+ * 获取 WxAccessToken
+ * @return
+ */
+ public static String getWxAccessToken() {
+ //参数
+ Map<String, String> params = new HashMap<>();
+ params.put("grant_type", "client_credential");
+ params.put("appid", WX_APP_ID);
+ params.put("secret", WX_SECRET);
+ String result = HttpClientUtils.doGet(WX_GET_ACCESS_TOKEN, params);
+ JSONObject jsonObject = JSON.parseObject(result);
+ String accessToken = jsonObject.getString("access_token");
+ return accessToken;
+ }
+
+
+
+ /**
+ * 压缩图片
+ * @param multipartFile
+ */
+ public static InputStream compressImage(MultipartFile multipartFile){
+ //*************对不是jpg格式的图片转换成jpg格式***************
+ //获取文件名后缀,判断其格式
+ int begin = multipartFile.getOriginalFilename().lastIndexOf(".");
+ int last = multipartFile.getOriginalFilename().length();
+ //获得文件后缀名
+ String houzuiFileName = multipartFile.getOriginalFilename().substring(begin, last);
+ //创建临时文件
+ File tempFile = new File(multipartFile.getOriginalFilename());
+ //写入临时File文件 tempFile,将multipartFile转换成File
+ try {
+ //import org.apache.commons.io.FileUtils;
+ FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), tempFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ //如果文件不是jpg格式,转换其格式
+ if (!"jpg".equalsIgnoreCase(houzuiFileName)){
+ //ImageUtils是一个工具类,下面给出
+ //将png格式转换成jpg,输出到tempFile
+ //测试OK
+ ImageUtils.convert(multipartFile.getOriginalFilename(), "jpg", tempFile.getAbsolutePath());
+ //*************对不是jpg格式的图片转换成jpg格式***************
+ }
+ try {
+ //压缩图片
+ BufferedImage bufferedImage = Thumbnails.of(tempFile)
+ .size(740, 1330)//指定压缩之后的图片尺寸
+ .outputQuality(0.8f)//图片压缩质量
+ .asBufferedImage();
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ ImageIO.write(bufferedImage, "jpg", os);
+ InputStream inputStream = new ByteArrayInputStream(os.toByteArray());
+// System.out.println(inputStream);
+ return inputStream;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }finally {
+// 会在本地产生临时文件,用完后需要删除
+ if (tempFile.exists()) {
+ tempFile.delete();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/springblade/modules/resource/endpoint/OssEndpoint.java b/src/main/java/org/springblade/modules/resource/endpoint/OssEndpoint.java
index 413f2f0..c40fa1e 100644
--- a/src/main/java/org/springblade/modules/resource/endpoint/OssEndpoint.java
+++ b/src/main/java/org/springblade/modules/resource/endpoint/OssEndpoint.java
@@ -16,10 +16,14 @@
*/
package org.springblade.modules.resource.endpoint;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.JSONPObject;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springblade.common.utils.ImageUtils;
+import org.springblade.common.utils.WeiXinSecurityUtil;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.oss.model.BladeFile;
import org.springblade.core.oss.model.OssFile;
@@ -37,6 +41,15 @@
import org.springblade.modules.resource.service.IAttachService;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
/**
* 对象存储端点
@@ -158,7 +171,7 @@
// 修改link
changeLink(bladeFile);
// 返回
- return R.data(bladeFile);
+ return R.data(200,bladeFile,"");
}
/**
@@ -227,15 +240,27 @@
*
* @param file 文件
* @param prefixPath 文件
+ * @param isCheck 是否校验
* @return ObjectStat
*/
@SneakyThrows
@PostMapping("/put-file-by-prefix-path")
- public R<BladeFile> putFileByPrefixPath(@RequestParam MultipartFile file,@RequestParam(required = false) String prefixPath) {
+ public R<BladeFile> putFileByPrefixPath(@RequestParam MultipartFile file,
+ @RequestParam(required = false) String prefixPath,
+ @RequestParam(required = false) Integer isCheck) {
+ // 不传默认不检验
+ if(null!=isCheck){
+ String checkResult = WeiXinSecurityUtil.checkImg(file);
+ JSONObject jsonObject = JSONObject.parseObject(checkResult);
+ Integer errorCode = Integer.parseInt(jsonObject.getString("errcode"));
+ if (errorCode!=0){
+ return R.data(201,null,"图片存在敏感内容,请更换其他图片!");
+ }
+ }
BladeFile bladeFile = ossBuilder.templateByPrefixPath(prefixPath).putFile(file.getOriginalFilename(), file.getInputStream());
// 修改link
changeLink(bladeFile);
- return R.data(bladeFile);
+ return R.data(200,bladeFile,"");
}
/**
@@ -243,11 +268,23 @@
*
* @param file 文件
* @param prefixPath 文件
+ * @param isCheck 是否校验
* @return ObjectStat
*/
@SneakyThrows
@PostMapping("/put-file-attach-by-prefix-path")
- public R<BladeFile> putFileAttachByPrefixPath(@RequestParam MultipartFile file,@RequestParam(required = false) String prefixPath) {
+ public R<BladeFile> putFileAttachByPrefixPath(@RequestParam MultipartFile file,
+ @RequestParam(required = false) String prefixPath,
+ @RequestParam(required = false) Integer isCheck) {
+ // 不传默认不检验
+ if(null!=isCheck){
+ String checkResult = WeiXinSecurityUtil.checkImg(file);
+ JSONObject jsonObject = JSONObject.parseObject(checkResult);
+ Integer errorCode = Integer.parseInt(jsonObject.getString("errcode"));
+ if (errorCode!=0){
+ return R.data(201,null,"图片存在敏感内容,请更换其他图片!");
+ }
+ }
String fileName = file.getOriginalFilename();
BladeFile bladeFile = ossBuilder.templateByPrefixPath(prefixPath).putFile(file.getOriginalFilename(), file.getInputStream());
Long attachId = buildAttach(fileName, file.getSize(), bladeFile);
@@ -255,7 +292,7 @@
bladeFile.setAttachId(attachId);
// 修改link
changeLink(bladeFile);
- return R.data(bladeFile);
+ return R.data(200,bladeFile,"");
}
/**
@@ -329,4 +366,5 @@
return R.success("操作成功");
}
+
}
diff --git a/src/main/java/org/springblade/modules/resource/utils/ImageUtil.java b/src/main/java/org/springblade/modules/resource/utils/ImageUtil.java
new file mode 100644
index 0000000..797cb32
--- /dev/null
+++ b/src/main/java/org/springblade/modules/resource/utils/ImageUtil.java
@@ -0,0 +1,171 @@
+package org.springblade.modules.resource.utils;
+import lombok.extern.slf4j.Slf4j;
+import javax.imageio.ImageIO;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.net.URL;
+
+/**
+ * @author arsn
+ * @className ImageUtil
+ * @Description 图片缩放
+ **/
+@Slf4j
+public class ImageUtil {
+ /**
+ * 缩放比例系数
+ */
+ private static double SCALING = 0.56;
+ /**
+ * 符合base64的宽
+ */
+ private static int MAX_WIDTH = 560;
+ /**
+ * 最大高
+ */
+ private static int MAX_HEIGHT = 1000;
+
+ /**
+ * @Author 小帅丶
+ * @Description 根据图片公网地址转BufferedImage
+ * @Date 2020/9/29 10:52
+ * @param url 图片公网地址
+ * @return java.awt.image.BufferedImage
+ **/
+ public static BufferedImage imgUrlConvertBufferedImage(String url) throws Exception {
+ URL urls = new URL(url);
+ Image image = Toolkit.getDefaultToolkit().getImage(urls);
+ BufferedImage bufferedImage = toBufferedImage(image);
+ return bufferedImage;
+ }
+ /**
+ * @Author 小帅丶
+ * @Description 根据BufferedImage处理图片并返回byte[]
+ * @Date 2020/9/29 10:55
+ * @param bufferedImage
+ * @return byte[]
+ **/
+ public static byte[] zoomImageByte(BufferedImage bufferedImage) throws Exception {
+ ByteArrayOutputStream outputStreamZoom = new ByteArrayOutputStream();
+ ByteArrayOutputStream outputStreamSource = new ByteArrayOutputStream();
+ ImageIO.write(bufferedImage, "jpg", outputStreamSource);
+ BufferedImage bufferedImageZoom = zoomImage(outputStreamSource.toByteArray());
+ //写入缩减后的图片
+ ImageIO.write(bufferedImageZoom, "jpg", outputStreamZoom);
+ return outputStreamZoom.toByteArray();
+ }
+
+ /**
+ * @Author 小帅丶
+ * @Description 根据byte[]处理图片并返回byte[]
+ * @Date 2020/9/29 10:55
+ * @param src
+ * @return byte[]
+ **/
+ public static byte[] zoomImageByte(byte[] src) throws Exception {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ BufferedImage bufferedImage = zoomImage(src);
+ //写入缩减后的图片
+ ImageIO.write(bufferedImage, "jpg", outputStream);
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * 图片缩放 仅适用于微信内容图片安全检测使用
+ *
+ * @param src 为源文件byte
+ */
+ public static BufferedImage zoomImage(byte[] src) throws Exception {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(src);
+ double wr = 0, hr = 0;
+ BufferedImage bufferedImage = null;
+ //读取图片
+ BufferedImage bufImg = ImageIO.read(in);
+ int height = bufImg.getHeight();
+ int width = bufImg.getWidth();
+ int cHeight = height;
+ int cWidth = width;
+ double Scaling = width / height;
+ if (Scaling < SCALING) {
+ if (height > MAX_HEIGHT) {
+ cHeight = MAX_HEIGHT;
+ cWidth = (width * MAX_HEIGHT) / height;
+ }
+ //以宽为缩放比例
+ } else {
+ if (width > MAX_WIDTH) {
+ cWidth = MAX_WIDTH;
+ cHeight = (height * MAX_WIDTH) / width;
+ }
+ }
+ //获取缩放后的宽高
+ log.info("宽{},高{}", cWidth, cHeight);
+ //设置缩放目标图片模板
+ Image Itemp = bufImg.getScaledInstance(width, cHeight, BufferedImage.SCALE_SMOOTH);
+ //获取缩放比例
+ wr = cWidth * 1.0 / width;
+ hr = cHeight * 1.0 / height;
+ log.info("宽比例{},高比例{}", wr, hr);
+ AffineTransformOp ato = new AffineTransformOp(AffineTransform.getScaleInstance(wr, hr), null);
+ Itemp = ato.filter(bufImg, null);
+ try {
+ //写入缩减后的图片
+ ImageIO.write((BufferedImage) Itemp, "jpg", outputStream);
+ ByteArrayInputStream inNew = new ByteArrayInputStream(outputStream.toByteArray());
+ bufferedImage = ImageIO.read(inNew);
+ } catch (Exception ex) {
+ log.info("缩放图片异常{}", ex.getMessage());
+ } finally {
+ if (null != outputStream) {
+ outputStream.close();
+ }
+ if (null != in) {
+ in.close();
+ }
+ }
+ return bufferedImage;
+ }
+
+ /**
+ * @Description Image转BufferedImage
+ * @param image 通过url获取的image对象
+ * @return java.awt.image.BufferedImage
+ **/
+ public static BufferedImage toBufferedImage(Image image) {
+ if (image instanceof BufferedImage) {
+ return (BufferedImage) image;
+ }
+ // This code ensures that all the pixels in the image are loaded
+ image = new ImageIcon(image).getImage();
+ BufferedImage bimage = null;
+ GraphicsEnvironment ge = GraphicsEnvironment
+ .getLocalGraphicsEnvironment();
+ try {
+ int transparency = Transparency.OPAQUE;
+ GraphicsDevice gs = ge.getDefaultScreenDevice();
+ GraphicsConfiguration gc = gs.getDefaultConfiguration();
+ bimage = gc.createCompatibleImage(image.getWidth(null),
+ image.getHeight(null), transparency);
+ } catch (HeadlessException e) {
+ // The system does not have a screen
+ }
+ if (bimage == null) {
+ // Create a buffered image using the default color model
+ int type = BufferedImage.TYPE_INT_RGB;
+ bimage = new BufferedImage(image.getWidth(null),
+ image.getHeight(null), type);
+ }
+ // Copy image to buffered image
+ Graphics g = bimage.createGraphics();
+ // Paint the image onto the buffered image
+ g.drawImage(image, 0, 0, null);
+ g.dispose();
+ return bimage;
+ }
+}
--
Gitblit v1.9.3