|
|
@@ -2,8 +2,10 @@ package com.github.microservice.auth.server.core.service.local;
|
|
|
|
|
|
import com.github.microservice.auth.client.content.ResultContent;
|
|
|
import com.github.microservice.auth.client.content.ResultState;
|
|
|
-import com.github.microservice.auth.server.core.conf.RedisearchConf;
|
|
|
-import com.github.microservice.auth.server.core.model.RedisearchResultModel;
|
|
|
+import com.github.microservice.auth.security.helper.AuthHelper;
|
|
|
+import com.github.microservice.auth.server.core.helper.FaceHelper;
|
|
|
+import com.github.microservice.auth.server.core.model.FacePutResultsModel;
|
|
|
+import com.github.microservice.auth.server.core.model.FaceSearchResultModel;
|
|
|
import com.github.microservice.auth.client.model.UserFaceQueryModel;
|
|
|
import com.github.microservice.auth.client.model.UserFaceUploadModel;
|
|
|
import com.github.microservice.auth.client.service.UserFaceService;
|
|
|
@@ -11,10 +13,7 @@ import com.github.microservice.auth.server.core.dao.UserDao;
|
|
|
import com.github.microservice.auth.server.core.dao.UserFaceDao;
|
|
|
import com.github.microservice.auth.server.core.domain.User;
|
|
|
import com.github.microservice.auth.server.core.domain.UserFace;
|
|
|
-import com.github.microservice.auth.server.core.model.ResultsModel;
|
|
|
-import com.github.microservice.auth.server.core.util.FaceUtil;
|
|
|
import com.github.microservice.core.util.JsonUtil;
|
|
|
-import com.github.microservice.core.util.bean.BeanUtil;
|
|
|
import com.mongodb.client.gridfs.GridFSFindIterable;
|
|
|
import com.mongodb.client.gridfs.model.GridFSFile;
|
|
|
import jakarta.validation.constraints.NotNull;
|
|
|
@@ -22,17 +21,11 @@ import lombok.Cleanup;
|
|
|
import lombok.SneakyThrows;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
-import org.apache.kafka.common.protocol.types.Field;
|
|
|
-import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-import org.springframework.beans.factory.annotation.Value;
|
|
|
-import org.springframework.cglib.beans.BeanMap;
|
|
|
import org.springframework.data.mongodb.core.query.Criteria;
|
|
|
import org.springframework.data.mongodb.core.query.Query;
|
|
|
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
|
|
|
-import org.springframework.security.crypto.password.PasswordEncoder;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
-import org.springframework.web.client.RestTemplate;
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
import redis.clients.jedis.JedisPooled;
|
|
|
|
|
|
@@ -43,9 +36,7 @@ import java.io.ByteArrayInputStream;
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
import java.io.IOException;
|
|
|
import java.io.InputStream;
|
|
|
-import java.math.BigDecimal;
|
|
|
import java.net.http.HttpResponse;
|
|
|
-import java.nio.ByteBuffer;
|
|
|
import java.util.*;
|
|
|
import java.util.List;
|
|
|
|
|
|
@@ -63,18 +54,8 @@ public class UserFaceServiceImpl implements UserFaceService {
|
|
|
UserFaceDao userFaceDao;
|
|
|
|
|
|
@Autowired
|
|
|
- RestTemplate restTemplate;
|
|
|
+ FaceHelper faceHelper;
|
|
|
|
|
|
- @Value("${faceImage.width}")
|
|
|
- private int width;
|
|
|
-
|
|
|
- @Value("${faceImage.height}")
|
|
|
- private int height;
|
|
|
- @Autowired
|
|
|
- private PasswordEncoder passwordEncoder;
|
|
|
-
|
|
|
- @Autowired
|
|
|
- RedisearchConf redisearchConf;
|
|
|
|
|
|
/**
|
|
|
* 上传人脸图片
|
|
|
@@ -93,133 +74,48 @@ public class UserFaceServiceImpl implements UserFaceService {
|
|
|
if (user == null) {
|
|
|
return ResultContent.build(ResultState.UserNotExists);
|
|
|
}
|
|
|
+
|
|
|
+ String key = user.getId() + "_" + System.currentTimeMillis();
|
|
|
+ UserFace userFace = new UserFace();
|
|
|
boolean isUpdate = StringUtils.isNotBlank(userFaceUploadModel.getUpdateFaceFileId());
|
|
|
if (isUpdate) {
|
|
|
//替换原人脸图片
|
|
|
- if (userFaceDao.existsByUserIdAndFaceFSId(userFaceUploadModel.getUserId(), userFaceUploadModel.getUpdateFaceFileId())) {
|
|
|
+ userFace = userFaceDao.findTopByUserIdAndFaceFSId(userFaceUploadModel.getUserId(), userFaceUploadModel.getUpdateFaceFileId());
|
|
|
+ if (userFace == null) {
|
|
|
return ResultContent.build(ResultState.FaceNotExists);
|
|
|
}
|
|
|
+ key = userFace.getFaceDataKey();
|
|
|
+ HttpResponse<String> delete = faceHelper.delete(key);
|
|
|
+ if (delete.statusCode() != 200) {
|
|
|
+ return ResultContent.build(ResultState.FaceDataDeleteFail);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- MultipartFile file = userFaceUploadModel.getFile();
|
|
|
- InputStream inputStream = file.getInputStream();
|
|
|
-
|
|
|
-
|
|
|
- //TODO 调整图片大小
|
|
|
-// byte[] bytes = imageResizer(userFaceUploadModel.getUserFace());
|
|
|
- //TODO 上传人脸库,获取向量
|
|
|
- HttpResponse<String> response = FaceUtil.uploadFs("http://172.24.50.53:5001/v2/represent", inputStream, "yunet");
|
|
|
+ //上传人脸库,获取向量
|
|
|
+ String fileBase64 = userFaceUploadModel.getFile();
|
|
|
+ byte[] decode = Base64.getDecoder().decode(fileBase64);
|
|
|
+ @Cleanup InputStream inputStream = new ByteArrayInputStream(decode);
|
|
|
|
|
|
+ HttpResponse<String> response = faceHelper.uploadFs(inputStream, key);
|
|
|
if (response.statusCode() != 200) {
|
|
|
return ResultContent.build(ResultState.Fail, "人脸特征提取失败");
|
|
|
}
|
|
|
|
|
|
- final String body = response.body();
|
|
|
-
|
|
|
- ResultsModel resultsModel = JsonUtil.toObject(body, ResultsModel.class);
|
|
|
+ FacePutResultsModel resultsModel = JsonUtil.toObject(response.body(), FacePutResultsModel.class);
|
|
|
System.out.println(resultsModel);
|
|
|
|
|
|
|
|
|
-
|
|
|
- System.out.println(body);
|
|
|
- List<Double> embedding = resultsModel.getResults().get(0).getEmbedding();
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- JedisPooled jedis = new JedisPooled(redisearchConf.getHost(), redisearchConf.getPort(), null, redisearchConf.getPassword());
|
|
|
-
|
|
|
- // 动态设置变量
|
|
|
- String indexName = redisearchConf.getIndexName();
|
|
|
- String prefix = redisearchConf.getPrefix();
|
|
|
- String fieldName = redisearchConf.getFieldName();
|
|
|
- String dim = redisearchConf.getDim();
|
|
|
- initIndex(jedis, indexName, prefix, fieldName, dim);
|
|
|
-
|
|
|
-
|
|
|
- // 将 List<Float> 转换为 float[]
|
|
|
- double[] vector = listToFloatArray(embedding);
|
|
|
- // 归一化向量
|
|
|
- double norm = calculateNorm(vector);
|
|
|
- for (int i = 0; i < vector.length; i++) {
|
|
|
- vector[i] /= norm;
|
|
|
- }
|
|
|
-
|
|
|
- // 转换为字节数组 (FLOAT32,每个值占 4 字节)
|
|
|
- ByteBuffer buffer = ByteBuffer.allocate(vector.length * Double.BYTES);
|
|
|
- for (double value : vector) {
|
|
|
- buffer.putDouble(value);
|
|
|
- }
|
|
|
-
|
|
|
- String key = prefix + user.getId() + System.currentTimeMillis();
|
|
|
-
|
|
|
- long hset = jedis.hset(key.getBytes(), fieldName.getBytes(), buffer.array());
|
|
|
-
|
|
|
- System.out.println(hset);
|
|
|
- jedis.close();
|
|
|
//图片入库
|
|
|
String fileId = gridFsTemplate.store(inputStream, UUID.randomUUID().toString(), Map.of("userId", userFaceUploadModel.getUserId())).toHexString();
|
|
|
- UserFace userFace = new UserFace();
|
|
|
- if (isUpdate) {
|
|
|
- userFace = userFaceDao.findTopByUserIdAndFaceFSId(userFaceUploadModel.getUserId(), userFaceUploadModel.getUpdateFaceFileId());
|
|
|
- }
|
|
|
+
|
|
|
//向量入库
|
|
|
userFace.setUserId(user.getId());
|
|
|
userFace.setFaceFSId(fileId);
|
|
|
- //设置向量
|
|
|
- userFace.setVector(embedding);
|
|
|
userFace.setFaceDataKey(key);
|
|
|
userFaceDao.save(userFace);
|
|
|
return ResultContent.buildContent(fileId);
|
|
|
}
|
|
|
|
|
|
- private static void initIndex(JedisPooled jedis, String indexName, String prefix, String fieldName, String dim) {
|
|
|
- try {
|
|
|
- Map<String, Object> stringObjectMap = jedis.ftInfo(indexName);
|
|
|
- //TODO 检查参数是否正确
|
|
|
- }catch (Exception e) {
|
|
|
- //创建索引
|
|
|
- // Lua 脚本模板
|
|
|
- String createIndexScript = "local indexName = KEYS[1] " +
|
|
|
- "local prefix = KEYS[2] " +
|
|
|
- "local fieldName = KEYS[3] " +
|
|
|
- "local dim = ARGV[1] " +
|
|
|
- "redis.call('FT.CREATE', indexName, 'ON', 'HASH', 'PREFIX', '1', prefix, " +
|
|
|
- "'SCHEMA', fieldName, 'VECTOR', 'FLAT', '6', " +
|
|
|
- "'TYPE', 'FLOAT64', 'DIM', dim, 'DISTANCE_METRIC', 'COSINE')";
|
|
|
-
|
|
|
-
|
|
|
- String a = """
|
|
|
-
|
|
|
- """;
|
|
|
-
|
|
|
- // 执行 Lua 脚本并传递变量
|
|
|
- Object result = jedis.eval(createIndexScript,
|
|
|
- // KEYS 参数:索引名、前缀、字段名
|
|
|
- List.of(indexName, prefix, fieldName), List.of(dim));
|
|
|
- System.out.println("Index created successfully: " + result);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private static double calculateNorm(double[] vector) {
|
|
|
- double sum = 0;
|
|
|
- for (double v : vector) {
|
|
|
- sum += v * v; // 对每个分量平方求和
|
|
|
- }
|
|
|
- return (double) Math.sqrt(sum); // 求平方和的平方根
|
|
|
- }
|
|
|
-
|
|
|
- public static double[] listToFloatArray(List<Double> list) {
|
|
|
- // 创建一个与 List 大小相同的 float 数组
|
|
|
- double[] array = new double[list.size()];
|
|
|
-
|
|
|
- // 使用普通的 for 循环将 List 中的每个元素放入数组中
|
|
|
- for (int i = 0; i < list.size(); i++) {
|
|
|
- array[i] = list.get(i); // 自动拆箱,将 Float 转换为 float
|
|
|
- }
|
|
|
-
|
|
|
- return array;
|
|
|
- }
|
|
|
|
|
|
/**
|
|
|
* 删除用户指定人脸图片
|
|
|
@@ -240,12 +136,11 @@ public class UserFaceServiceImpl implements UserFaceService {
|
|
|
}
|
|
|
|
|
|
//删除人脸库的人脸
|
|
|
- JedisPooled jedis = new JedisPooled(redisearchConf.getHost(), redisearchConf.getPort(), null, redisearchConf.getPassword());
|
|
|
+ HttpResponse<String> response = faceHelper.delete(userFace.getFaceDataKey());
|
|
|
|
|
|
- long del = jedis.del(userFace.getFaceDataKey());
|
|
|
- if (del == 0) {
|
|
|
+ if (response.statusCode() != 200) {
|
|
|
log.error("人脸库删除数据失败:key:{}", userFace.getFaceDataKey());
|
|
|
- return ResultContent.build(ResultState.Fail);
|
|
|
+ return ResultContent.build(ResultState.Fail, "人脸库数据删除失败");
|
|
|
}
|
|
|
userFaceDao.deleteById(userFace.getId());
|
|
|
return ResultContent.build(ResultState.Success);
|
|
|
@@ -289,133 +184,18 @@ public class UserFaceServiceImpl implements UserFaceService {
|
|
|
*/
|
|
|
@SneakyThrows
|
|
|
public ResultContent<Object> matches(MultipartFile file) {
|
|
|
- //TODO 上传人脸服务,获取向量
|
|
|
- InputStream inputStream = file.getInputStream();
|
|
|
-
|
|
|
- //TODO 调整图片大小
|
|
|
-// byte[] bytes = imageResizer(userFaceUploadModel.getUserFace());
|
|
|
- //TODO 上传人脸库,获取向量
|
|
|
- HttpResponse<String> response = FaceUtil.uploadFs("http://172.24.50.53:5001/v2/represent", inputStream, "yunet");
|
|
|
+ @Cleanup InputStream inputStream = file.getInputStream();
|
|
|
+ HttpResponse<String> response = faceHelper.search(inputStream, 1);
|
|
|
|
|
|
if (response.statusCode() != 200) {
|
|
|
return ResultContent.build(ResultState.Fail, "人脸特征提取失败");
|
|
|
}
|
|
|
-
|
|
|
- final String body = response.body();
|
|
|
-
|
|
|
- ResultsModel resultsModel = JsonUtil.toObject(body, ResultsModel.class);
|
|
|
- System.out.println(resultsModel);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- System.out.println(body);
|
|
|
- List<Double> embedding = resultsModel.getResults().get(0).getEmbedding();
|
|
|
- double[] vector = listToFloatArray(embedding);
|
|
|
-
|
|
|
- double norm = calculateNorm(vector);
|
|
|
- for (int i = 0; i < vector.length; i++) {
|
|
|
- vector[i] /= norm;
|
|
|
- }
|
|
|
-
|
|
|
- // 转换为字节数组 (FLOAT32,每个值占 4 字节)
|
|
|
- ByteBuffer buffer = ByteBuffer.allocate(vector.length * Double.BYTES);
|
|
|
- for (double value : vector) {
|
|
|
- buffer.putDouble(value);
|
|
|
- }
|
|
|
- // 打印字节数组的内容
|
|
|
- byte[] queryVector = buffer.array();
|
|
|
- System.out.println("queryVector.length");
|
|
|
- System.out.println(queryVector.length);
|
|
|
-
|
|
|
- // 定义查询参数
|
|
|
- String indexName = redisearchConf.getIndexName();
|
|
|
- String prefix = redisearchConf.getPrefix();
|
|
|
- String fieldName = redisearchConf.getFieldName();
|
|
|
- String dim = redisearchConf.getDim();
|
|
|
-// String queryVectorString = "\x8d5\xf7=\x91w:=*\x19h=\x05u\xc8=\xfa\x06\xb0=+\x00\xa8=\x96\x9b\x02>\xd5\xb4\x19>\xf1Pf=\xb5U\xf7=\xbc\xaf\x10>\xd9\x96\x18>\xa5hk=\xcd\xd3\x83=\x86\xcd\x18>\xbd\x80\x0b>A$\xf1=\xb2\xcf\xc5=K\xd4\xe2=\x83c\x01>\x02\x85\xbb=c$'=\x00-\x14>\x0c[i=\x80\x0b\xaf=\x15r\x0c>\xb2\x1f\xbf<\xe78\x8e=\x9b\xb9\xdf=\xce\xd9\xa3=\xe7\xaa\r>\xee\\\xaa=\x83\x95g=g/R=x\xc0\x0b>\xfd\xe7\x07>M\xb8\x0f=\x05\xbcA=q\x0e\xbd=R\x98\x8c;b\xfe\x85=\xea%+=\xe7\x98\x81:0H\x07>\xd9g\x04>\xe5\xe3\x8e=oM]=%\xbe\x92<\x1e\xf7\x05>\x11\xa9\x1a>\xce1\xe9<Z\x8d\x0e>\xe0<\x07>\xcar\x86=\xde\x0e\x9e=l\x8e\xf6=\xa9\x04\xa7=7\xe9G=\n[\x05<\xcd\xb3\x1c=\xf26\xe2=\xfd\xbd\xa4=\x94\x95\xea=\xb3\x9f\xa7<*Z\xfb;o\xbb\xb7<\xae\xea\xdb=\x03\x82\xcb<\xc8\x12\x1a<\xfa\xf9\x91<\xacJ\x18=\x18\xcb\xb0=n\x87\x92=6j\xf0=\xa7\xf0\x92<]!\xb4=\xf8\x01\x85=\xaf\xfa\xd7=\xbbE\xe5;f\x9d\xeb<^\xe1\x00>\xa3\xb9\x93=\x83\xcb\x14>\xdb\x86\x1a>\xe1\xa0\xd7=Cu*=9\xfd\x8f=e\xe2\xe7<y1.=2T\x91=59\x92=\xd8\xb5\xbd=\xd5\xf1\xa1=\xf4\xd6\x08>m\xd3K<\x1b\xc5M=\x93#\xb2=\xc0\x8b\xb4=\xa0Ph=\xe4s\x92=\xd7\x93\xb4=\xa3x\\;Q\x84\x08>\x8b&\xdc<a\x0e\x1d=\x83\x9c\x9e<\x9a\x84\xac<\xceS\xdf=\x8bT\xab;\xa7\xfd\r=\x88j\x10<\xcd\x07\xee<\x07\x8e\x81<t^\xdc=\xcf\xcf\xbd=\xeb\x92\xa1<[\x88\xd7=\xf0\xbb\x00>\x8e*\x18>\x82\xad\x11>yG\xbb<g\x07\x06>\xa08\x8d<\x01C\x1b>\xc7\xfd:=\x0b\x93\x10;\xbce>=\xb9\xdba=";
|
|
|
- String topK = "1"; // 返回Top 10个结果
|
|
|
-// String vectorField = "doc_embedding"; // 向量字段名称
|
|
|
-
|
|
|
- String queryScript = "local indexName = KEYS[1] "
|
|
|
- + "local queryVector = ARGV[1] "
|
|
|
- + "local topK = tonumber(ARGV[2]) "
|
|
|
- + "local vectorField = ARGV[3] "
|
|
|
- + "local query = '*=>[KNN ' .. topK .. ' @' .. vectorField .. ' $BLOB AS score]' "
|
|
|
- + "local result = redis.call('FT.SEARCH', indexName, query, "
|
|
|
- + "'PARAMS', '2', 'BLOB', queryVector, "
|
|
|
- + "'RETURN', '1', 'score', "
|
|
|
- + "'SORTBY', 'score', "
|
|
|
- + "'DIALECT', '2') "
|
|
|
- + "return result";
|
|
|
- // 执行Redis查询
|
|
|
-
|
|
|
-
|
|
|
- JedisPooled jedis = new JedisPooled( redisearchConf.getHost(), redisearchConf.getPort(), null, redisearchConf.getPassword());
|
|
|
-
|
|
|
- // 动态设置变量
|
|
|
-
|
|
|
- initIndex(jedis, indexName, prefix, fieldName, dim);
|
|
|
-
|
|
|
- Object result = jedis.eval(queryScript.getBytes(), 1, indexName.getBytes(), queryVector, topK.getBytes(), fieldName.getBytes());
|
|
|
- // 关闭连接
|
|
|
- jedis.close();
|
|
|
- // 打印结果
|
|
|
- System.out.println("Query Result: " + JsonUtil.toJson(result));
|
|
|
- RedisearchResultModel redisearchResultModel = processQueryResult(result);
|
|
|
- //TODO 解析结果,返回userId
|
|
|
- if (redisearchResultModel.getCount() == 0L) {
|
|
|
- return ResultContent.build(ResultState.FaceNoMatches);
|
|
|
- }
|
|
|
-
|
|
|
- if (redisearchResultModel.getScoreValue().equals("nan") || redisearchResultModel.getScoreValue().equals("-nan")) {
|
|
|
- return ResultContent.build(ResultState.FaceNoMatches);
|
|
|
- }
|
|
|
-
|
|
|
- BigDecimal scoreValue = new BigDecimal(redisearchResultModel.getScoreValue());
|
|
|
- if (scoreValue.compareTo(BigDecimal.ZERO) == 0 || scoreValue.compareTo(new BigDecimal(redisearchConf.getScore())) > 0) {
|
|
|
- String dataKey = redisearchResultModel.getDataKey();
|
|
|
- dataKey = dataKey.replace(prefix, "");
|
|
|
- String userId = dataKey.substring(0, 24);
|
|
|
- redisearchResultModel.setUserId(userId);
|
|
|
- return ResultContent.buildContent(BeanUtil.bean2Map(redisearchResultModel));
|
|
|
- }else {
|
|
|
- return ResultContent.build(ResultState.FaceNoMatches);
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private static RedisearchResultModel processQueryResult(Object result) {
|
|
|
- RedisearchResultModel redisearchResultModel = new RedisearchResultModel();
|
|
|
-
|
|
|
- if (result instanceof List){
|
|
|
- List<Object> list = (List) result;
|
|
|
- for (Object item : list) {
|
|
|
- if (item instanceof byte[]) {// data key
|
|
|
- byte[] bytes = ((byte[]) item);
|
|
|
- System.out.println("Found vector: " + new String(bytes)); // 可以根据需要进一步处理
|
|
|
- redisearchResultModel.setDataKey(new String(bytes));
|
|
|
- } else if (item instanceof Long){// 返回的结果数
|
|
|
- System.out.println("Found Long: " + item);
|
|
|
- redisearchResultModel.setCount((Long)item);
|
|
|
- }else if (item instanceof List) {// 对应的 距离
|
|
|
- List<Object> itemList = (List) item;
|
|
|
- for (Object item1 : itemList) {
|
|
|
- if (item1 instanceof byte[]){
|
|
|
- byte[] bytes = ((byte[]) item1);
|
|
|
- System.out.println("Found vector: " + new String(bytes)); // 可以根据需要进一步处理
|
|
|
- String str = new String(bytes);
|
|
|
- if (str.equals("score")){
|
|
|
- redisearchResultModel.setScoreKey(str);
|
|
|
- }else {
|
|
|
- redisearchResultModel.setScoreValue(str);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return redisearchResultModel;
|
|
|
+ FaceSearchResultModel resultsModel = JsonUtil.toObject(response.body(), FaceSearchResultModel.class);
|
|
|
+ List<FaceSearchResultModel.Item> items = resultsModel.getItems().stream().map(item -> {
|
|
|
+ item.setKey(item.getKey().substring(0, item.getKey().lastIndexOf("_")));
|
|
|
+ return item;
|
|
|
+ }).toList();
|
|
|
+ return ResultContent.buildContent(Map.of("items", items));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -437,56 +217,4 @@ public class UserFaceServiceImpl implements UserFaceService {
|
|
|
return Base64.getEncoder().encodeToString(bytes);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- private byte[] imageResizer(String base64Image) {
|
|
|
- // 解码 Base64 字符串为 BufferedImage
|
|
|
- BufferedImage originalImage = decodeBase64ToImage(base64Image);
|
|
|
-
|
|
|
- // 调整图像大小
|
|
|
- BufferedImage resizedImage = resizeImage(originalImage, width, height);
|
|
|
-
|
|
|
- // 将调整后的图像转换为字节组
|
|
|
- return encodeImageToBytes(resizedImage);
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-// public static void resizeImage(InputStream inputStream, int width, int height) throws IOException {
|
|
|
-// BufferedImage inputImage = ImageIO.read(inputStream);
|
|
|
-//
|
|
|
-// // 创建一个输出的 BufferedImage
|
|
|
-// BufferedImage outputImage = new BufferedImage(width, height, inputImage.getType());
|
|
|
-//
|
|
|
-// // 使用 Graphics2D 绘制缩放后的图片
|
|
|
-// Graphics2D g2d = outputImage.createGraphics();
|
|
|
-// g2d.drawImage(inputImage, 0, 0, width, height, null);
|
|
|
-// g2d.dispose();
|
|
|
-//
|
|
|
-// // 输出到文件
|
|
|
-// String formatName = outputFile.getName().substring(outputFile.getName().lastIndexOf(".") + 1);
|
|
|
-// ImageIO.write(outputImage, formatName, outputFile);
|
|
|
-// }
|
|
|
-
|
|
|
- private static BufferedImage resizeImage(BufferedImage originalImage, int width, int height) {
|
|
|
- BufferedImage resizedImage = new BufferedImage(width, height, originalImage.getType());
|
|
|
- Graphics2D g = resizedImage.createGraphics();
|
|
|
- g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
|
|
- g.drawImage(originalImage, 0, 0, width, height, null);
|
|
|
- g.dispose();
|
|
|
-
|
|
|
- return resizedImage;
|
|
|
- }
|
|
|
-
|
|
|
- @SneakyThrows
|
|
|
- private static BufferedImage decodeBase64ToImage(String base64Image) {
|
|
|
- byte[] imageBytes = Base64.getDecoder().decode(base64Image);
|
|
|
- @Cleanup ByteArrayInputStream bais = new ByteArrayInputStream(imageBytes);
|
|
|
- return ImageIO.read(bais);
|
|
|
- }
|
|
|
-
|
|
|
- @SneakyThrows
|
|
|
- private static byte[] encodeImageToBytes(BufferedImage image) {
|
|
|
- @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
- ImageIO.write(image, "png", baos);
|
|
|
- return baos.toByteArray();
|
|
|
- }
|
|
|
}
|