图片转换工具开发
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package com.njcn.common.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
* @version 1.0
|
||||
* @data 2025/6/17 15:17
|
||||
*/
|
||||
public class BinaryDataConverter {
|
||||
|
||||
/**
|
||||
* 使用Base64编码转换二进制数据为字符串
|
||||
* 推荐使用,最安全可靠的方式
|
||||
*/
|
||||
public static String toBase64String(byte[] binaryData) {
|
||||
return Base64.getEncoder().encodeToString(binaryData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用Base64解码字符串为二进制数据
|
||||
*/
|
||||
public static byte[] fromBase64String(String base64String) {
|
||||
return Base64.getDecoder().decode(base64String);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用UTF-8编码转换二进制数据为字符串
|
||||
* 注意:仅适用于文本数据,不适用于二进制数据
|
||||
*/
|
||||
public static String toUtf8String(byte[] binaryData) {
|
||||
return new String(binaryData, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用UTF-8编码转换字符串为二进制数据
|
||||
*/
|
||||
public static byte[] fromUtf8String(String text) {
|
||||
return text.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用十六进制字符串表示二进制数据
|
||||
* 适用于需要可读性的场景
|
||||
*/
|
||||
public static String toHexString(byte[] binaryData) {
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : binaryData) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从十六进制字符串转换为二进制数据
|
||||
*/
|
||||
public static byte[] fromHexString(String hexString) {
|
||||
int len = hexString.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
|
||||
+ Character.digit(hexString.charAt(i + 1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.njcn.common.utils.images;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
* @version 1.0
|
||||
* @data 2025/6/18 11:17
|
||||
*/
|
||||
public class BinFileReader {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String filePath = "C:\\Users\\hongawen\\Desktop\\temp\\qrc.bin";
|
||||
try (InputStream inputStream = new FileInputStream(filePath)) {
|
||||
printHexDump(inputStream);
|
||||
} catch (IOException e) {
|
||||
System.err.println("读取文件时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void printHexDump(InputStream stream) throws IOException {
|
||||
int bytesPerLine = 16;
|
||||
byte[] buffer = new byte[bytesPerLine];
|
||||
int bytesRead;
|
||||
long offset = 0;
|
||||
|
||||
while ((bytesRead = stream.read(buffer)) != -1) {
|
||||
// 打印偏移量 (地址)
|
||||
System.out.printf("%08X: ", offset);
|
||||
|
||||
// 打印十六进制值
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
StringBuilder asciiString = new StringBuilder();
|
||||
for (int i = 0; i < bytesPerLine; i++) {
|
||||
if (i < bytesRead) {
|
||||
byte b = buffer[i];
|
||||
hexString.append(String.format("%02X ", b));
|
||||
// 将非打印字符替换为 '.'
|
||||
asciiString.append(isPrintableChar(b) ? (char) b : '.');
|
||||
} else {
|
||||
// 用空格填充,如果最后一行不满
|
||||
hexString.append(" ");
|
||||
}
|
||||
if (i == 7) { // 在中间加一个空格,方便阅读
|
||||
hexString.append(" ");
|
||||
}
|
||||
}
|
||||
System.out.printf("%-50s %s%n", hexString.toString(), asciiString.toString());
|
||||
offset += bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPrintableChar(byte b) {
|
||||
return b >= 32 && b <= 126;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.njcn.common.utils.images;
|
||||
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -15,188 +16,81 @@ import java.nio.ByteOrder;
|
||||
*/
|
||||
public class ImageConverter {
|
||||
|
||||
|
||||
/**
|
||||
* 将BufferedImage转换为BMP格式的字节数组
|
||||
* @param image BufferedImage对象
|
||||
* @return BMP格式的字节数组
|
||||
* @throws IOException 如果转换过程中发生IO错误
|
||||
* 将一个 BufferedImage 对象转换为我们自定义的 BMPF .bin 格式。
|
||||
*/
|
||||
public static byte[] convertToBMP(BufferedImage image) throws IOException {
|
||||
public static byte[] convertToBinFormat(BufferedImage image) throws IOException {
|
||||
final int targetWidth = 148;
|
||||
final int targetHeight = 148;
|
||||
|
||||
// 1. 创建一个新的、与目标格式(16-bpp)匹配的、固定尺寸的画布
|
||||
BufferedImage newImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_USHORT_GRAY);
|
||||
Graphics2D g2d = newImage.createGraphics();
|
||||
|
||||
// 2. 用白色(0xFFFF)填充背景
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.fillRect(0, 0, targetWidth, targetHeight);
|
||||
|
||||
// 3. 将原始图像居中绘制到新画布上
|
||||
// Graphics2D 会自动处理颜色转换
|
||||
int x = (targetWidth - image.getWidth()) / 2;
|
||||
int y = (targetHeight - image.getHeight()) / 2;
|
||||
g2d.drawImage(image, x, y, null);
|
||||
g2d.dispose();
|
||||
|
||||
// 4. 后续操作基于新的、尺寸和位深度都正确的图像
|
||||
final int bpp = 16;
|
||||
// Stride: 每行像素数据的字节数。对于无填充的16bpp图像,等于 宽度 * 2
|
||||
final int stride = targetWidth * (bpp / 8);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (DataOutputStream out = new DataOutputStream(baos)) {
|
||||
// --- 写入文件头 (严格44字节) ---
|
||||
// 根据C代码 qrc_bitmap.pData = ...[44/4], 我们确认头部为44字节
|
||||
ByteBuffer headerBuffer = ByteBuffer.allocate(44);
|
||||
// 头部使用小端序
|
||||
headerBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
// 写入BMP文件头
|
||||
writeBMPHeader(baos, image.getWidth(), image.getHeight());
|
||||
// 0-3: 魔术字
|
||||
headerBuffer.put(new byte[]{'b', 'm', 'p', 'f'});
|
||||
// 4-5: 宽度
|
||||
headerBuffer.putShort((short) targetWidth);
|
||||
// 6-7: 高度
|
||||
headerBuffer.putShort((short) targetHeight);
|
||||
// 8-9: 行字节数
|
||||
headerBuffer.putShort((short) stride);
|
||||
// 10-11: 位深度
|
||||
headerBuffer.putShort((short) bpp);
|
||||
|
||||
// 写入像素数据
|
||||
for (int y = image.getHeight() - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < image.getWidth(); x++) {
|
||||
int rgb = image.getRGB(x, y);
|
||||
// 写入BGR格式(BMP格式要求)
|
||||
// B
|
||||
baos.write(rgb & 0xFF);
|
||||
// G
|
||||
baos.write((rgb >> 8) & 0xFF);
|
||||
// R
|
||||
baos.write((rgb >> 16) & 0xFF);
|
||||
}
|
||||
// 行对齐(每行必须是4字节的倍数)
|
||||
int padding = (4 - (image.getWidth() * 3) % 4) % 4;
|
||||
for (int i = 0; i < padding; i++) {
|
||||
baos.write(0);
|
||||
// 12-43: 剩余32字节,严格参照样本文件(out.txt)填充
|
||||
headerBuffer.put(new byte[]{
|
||||
// 12 bytes of zeros
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 20 bytes of specific data from sample
|
||||
(byte)0x44, (byte)0x3A, (byte)0x5C, (byte)0xD7, (byte)0xD4, (byte)0xB6, (byte)0xAF, (byte)0xBC,
|
||||
(byte)0xEC, (byte)0xB2, (byte)0xE2, (byte)0xC6, (byte)0xBD, (byte)0xCC, (byte)0xA8, (byte)0x5C,
|
||||
(byte)0x4C, (byte)0xAB, 0x00, 0x00
|
||||
});
|
||||
|
||||
// 将44字节头部一次性写入
|
||||
out.write(headerBuffer.array());
|
||||
|
||||
// --- 写入像素数据 ---
|
||||
// 为确保数据布局的绝对正确,放弃直接访问数据缓冲区(getDataBuffer),
|
||||
// 改为使用逐点扫描(getSample)的方式。这可以避免任何因Java内部内存对齐
|
||||
// 或行填充(padding)导致的图像数据错位问题。
|
||||
for (int r = 0; r < targetHeight; r++) {
|
||||
for (int c = 0; c < targetWidth; c++) {
|
||||
// getSample(x, y, band) 返回的是一个int, 但其值就是16位的ushort值
|
||||
int pixelValue = newImage.getRaster().getSample(c, r, 0);
|
||||
// 依旧使用大端序写入像素数据,这是上次测试得出的结论
|
||||
out.writeShort((short) pixelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将BMP格式的字节数组转换为BIN格式
|
||||
* @param bmpData BMP格式的字节数组
|
||||
* @return BIN格式的字节数组
|
||||
*/
|
||||
public static byte[] convertBMPToBIN(byte[] bmpData) {
|
||||
// 跳过BMP文件头(54字节)
|
||||
int offset = 54;
|
||||
int width = ByteBuffer.wrap(bmpData, 18, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
int height = ByteBuffer.wrap(bmpData, 22, 4).order(ByteOrder.LITTLE_ENDIAN).getInt();
|
||||
|
||||
// 计算每行的字节数(包括对齐)
|
||||
int rowSize = ((width * 3 + 3) / 4) * 4;
|
||||
|
||||
// 创建BIN数据数组
|
||||
byte[] binData = new byte[width * height];
|
||||
int binIndex = 0;
|
||||
|
||||
// 转换像素数据
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
int bmpIndex = offset + (height - 1 - y) * rowSize + x * 3;
|
||||
// 将BGR转换为灰度值
|
||||
int b = bmpData[bmpIndex] & 0xFF;
|
||||
int g = bmpData[bmpIndex + 1] & 0xFF;
|
||||
int r = bmpData[bmpIndex + 2] & 0xFF;
|
||||
// 使用加权平均计算灰度值
|
||||
int gray = (int) (0.299 * r + 0.587 * g + 0.114 * b);
|
||||
// 二值化(阈值设为128)
|
||||
binData[binIndex++] = (byte) (gray > 128 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
return binData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将BufferedImage直接转换为BIN格式
|
||||
* @param image BufferedImage对象
|
||||
* @return BIN格式的字节数组
|
||||
* @throws IOException 如果转换过程中发生IO错误
|
||||
*/
|
||||
public static byte[] convertToBIN(BufferedImage image) throws IOException {
|
||||
byte[] bmpData = convertToBMP(image);
|
||||
return convertBMPToBIN(bmpData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入BMP文件头
|
||||
* @param out 输出流
|
||||
* @param width 图片宽度
|
||||
* @param height 图片高度
|
||||
* @throws IOException 如果写入过程中发生IO错误
|
||||
*/
|
||||
private static void writeBMPHeader(OutputStream out, int width, int height) throws IOException {
|
||||
// 文件头(14字节)
|
||||
out.write('B');
|
||||
out.write('M');
|
||||
writeInt(out, 54 + width * height * 3); // 文件大小
|
||||
writeInt(out, 0); // 保留
|
||||
writeInt(out, 54); // 数据偏移量
|
||||
|
||||
// 信息头(40字节)
|
||||
writeInt(out, 40); // 信息头大小
|
||||
writeInt(out, width); // 宽度
|
||||
writeInt(out, height); // 高度
|
||||
writeShort(out, 1); // 颜色平面数
|
||||
writeShort(out, 24); // 每像素位数
|
||||
writeInt(out, 0); // 压缩方式
|
||||
writeInt(out, width * height * 3); // 图像数据大小
|
||||
writeInt(out, 0); // 水平分辨率
|
||||
writeInt(out, 0); // 垂直分辨率
|
||||
writeInt(out, 0); // 使用的颜色数
|
||||
writeInt(out, 0); // 重要颜色数
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入一个整数(小端序)
|
||||
* @param out 输出流
|
||||
* @param value 要写入的整数
|
||||
* @throws IOException 如果写入过程中发生IO错误
|
||||
*/
|
||||
private static void writeInt(OutputStream out, int value) throws IOException {
|
||||
out.write(value & 0xFF);
|
||||
out.write((value >> 8) & 0xFF);
|
||||
out.write((value >> 16) & 0xFF);
|
||||
out.write((value >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入一个短整数(小端序)
|
||||
* @param out 输出流
|
||||
* @param value 要写入的短整数
|
||||
* @throws IOException 如果写入过程中发生IO错误
|
||||
*/
|
||||
private static void writeShort(OutputStream out, int value) throws IOException {
|
||||
out.write(value & 0xFF);
|
||||
out.write((value >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将BufferedImage保存为BMP文件
|
||||
* @param image BufferedImage对象
|
||||
* @param filePath 保存路径
|
||||
* @throws IOException 如果保存过程中发生IO错误
|
||||
*/
|
||||
public static void saveAsBMP(BufferedImage image, String filePath) throws IOException {
|
||||
byte[] bmpData = convertToBMP(image);
|
||||
try (FileOutputStream fos = new FileOutputStream(filePath)) {
|
||||
fos.write(bmpData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将BufferedImage保存为BIN文件
|
||||
* @param image BufferedImage对象
|
||||
* @param filePath 保存路径
|
||||
* @throws IOException 如果保存过程中发生IO错误
|
||||
*/
|
||||
public static void saveAsBIN(BufferedImage image, String filePath) throws IOException {
|
||||
byte[] binData = convertToBIN(image);
|
||||
try (FileOutputStream fos = new FileOutputStream(filePath)) {
|
||||
fos.write(binData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将BMP数据保存为文件
|
||||
* @param bmpData BMP格式的字节数组
|
||||
* @param filePath 保存路径
|
||||
* @throws IOException 如果保存过程中发生IO错误
|
||||
*/
|
||||
public static void saveBMPToFile(byte[] bmpData, String filePath) throws IOException {
|
||||
try (FileOutputStream fos = new FileOutputStream(filePath)) {
|
||||
fos.write(bmpData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将BIN数据保存为文件
|
||||
* @param binData BIN格式的字节数组
|
||||
* @param filePath 保存路径
|
||||
* @throws IOException 如果保存过程中发生IO错误
|
||||
*/
|
||||
public static void saveBINToFile(byte[] binData, String filePath) throws IOException {
|
||||
try (FileOutputStream fos = new FileOutputStream(filePath)) {
|
||||
fos.write(binData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,13 @@
|
||||
<version>${druid.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--mariadb-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.mariadb.jdbc</groupId>-->
|
||||
<!-- <artifactId>mariadb-java-client</artifactId>-->
|
||||
<!-- <version>3.3.2</version> -->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!--mysql驱动-->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
|
||||
4
njcn-plugin/README.md
Normal file
4
njcn-plugin/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# 本模块用于收录各种插件工具
|
||||
## HttpClient模块
|
||||
|
||||
封装RestTemplate用于远程接口访问;
|
||||
36
njcn-plugin/RestTemplate-plugin/pom.xml
Normal file
36
njcn-plugin/RestTemplate-plugin/pom.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>njcn-plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>RestTemplate-plugin</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>njcn-common</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache HttpClient -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.13</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.njcn.http.config;
|
||||
|
||||
|
||||
import com.njcn.http.util.RestTemplateUtil;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
* @version 1.0
|
||||
* @data 2025/6/17 11:15
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
// 创建连接池管理器
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
|
||||
// 设置最大连接数
|
||||
connectionManager.setMaxTotal(200);
|
||||
// 设置每个主机的最大连接数
|
||||
connectionManager.setDefaultMaxPerRoute(20);
|
||||
|
||||
// 创建HttpClient
|
||||
CloseableHttpClient httpClient = HttpClients.custom()
|
||||
.setConnectionManager(connectionManager)
|
||||
.build();
|
||||
|
||||
// 创建HttpComponentsClientHttpRequestFactory
|
||||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
// 设置连接超时时间(毫秒)
|
||||
factory.setConnectTimeout(5000);
|
||||
// 设置读取超时时间(毫秒)
|
||||
factory.setReadTimeout(5000);
|
||||
// 设置连接不够用的等待时间(毫秒)
|
||||
factory.setConnectionRequestTimeout(2000);
|
||||
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RestTemplateUtil restTemplateUtil(RestTemplate restTemplate) {
|
||||
return new RestTemplateUtil(restTemplate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
package com.njcn.http.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
* @version 1.0
|
||||
* @data 2025/6/17 11:18
|
||||
*/
|
||||
@Slf4j
|
||||
public class RestTemplateUtil {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public RestTemplateUtil(RestTemplate restTemplate) {
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET请求
|
||||
*/
|
||||
public <T> T get(String url, Class<T> responseType) {
|
||||
return get(url, null, null, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET请求(带参数)
|
||||
*/
|
||||
public <T> T get(String url, Map<String, Object> params, Class<T> responseType) {
|
||||
return get(url, params, null, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET请求(带参数和请求头)
|
||||
*/
|
||||
public <T> T get(String url, Map<String, Object> params, HttpHeaders headers, Class<T> responseType) {
|
||||
try {
|
||||
// 构建URL(带参数)
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
|
||||
if (params != null) {
|
||||
params.forEach(builder::queryParam);
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
if (headers != null) {
|
||||
requestHeaders.addAll(headers);
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<?> requestEntity = new HttpEntity<>(requestHeaders);
|
||||
ResponseEntity<T> response = restTemplate.exchange(
|
||||
builder.build().encode().toUri(),
|
||||
HttpMethod.GET,
|
||||
requestEntity,
|
||||
responseType
|
||||
);
|
||||
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
log.error("GET请求异常: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("GET请求异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求(JSON数据)
|
||||
*/
|
||||
public <T> T postJson(String url, Object body, Class<T> responseType) {
|
||||
return postJson(url, body, null, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求(JSON数据,带请求头)
|
||||
*/
|
||||
public <T> T postJson(String url, Object body, HttpHeaders headers, Class<T> responseType) {
|
||||
try {
|
||||
// 构建请求头
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
|
||||
if (headers != null) {
|
||||
requestHeaders.addAll(headers);
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<?> requestEntity = new HttpEntity<>(body, requestHeaders);
|
||||
ResponseEntity<T> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
responseType
|
||||
);
|
||||
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
log.error("POST请求异常: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("POST请求异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求(表单数据)
|
||||
*/
|
||||
public <T> T postForm(String url, Map<String, Object> formData, Class<T> responseType) {
|
||||
return postForm(url, formData, null, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST请求(表单数据,带请求头)
|
||||
*/
|
||||
public <T> T postForm(String url, Map<String, Object> formData, HttpHeaders headers, Class<T> responseType) {
|
||||
try {
|
||||
// 构建表单数据
|
||||
MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
|
||||
if (formData != null) {
|
||||
formData.forEach((key, value) -> requestBody.add(key, value));
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
if (headers != null) {
|
||||
requestHeaders.addAll(headers);
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
|
||||
ResponseEntity<T> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
responseType
|
||||
);
|
||||
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
log.error("POST表单请求异常: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("POST表单请求异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT请求
|
||||
*/
|
||||
public <T> T put(String url, Object body, Class<T> responseType) {
|
||||
return put(url, body, null, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT请求(带请求头)
|
||||
*/
|
||||
public <T> T put(String url, Object body, HttpHeaders headers, Class<T> responseType) {
|
||||
try {
|
||||
// 构建请求头
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
requestHeaders.setContentType(MediaType.APPLICATION_JSON);
|
||||
if (headers != null) {
|
||||
requestHeaders.addAll(headers);
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<?> requestEntity = new HttpEntity<>(body, requestHeaders);
|
||||
ResponseEntity<T> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.PUT,
|
||||
requestEntity,
|
||||
responseType
|
||||
);
|
||||
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
log.error("PUT请求异常: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("PUT请求异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE请求
|
||||
*/
|
||||
public <T> T delete(String url, Class<T> responseType) {
|
||||
return delete(url, null, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE请求(带请求头)
|
||||
*/
|
||||
public <T> T delete(String url, HttpHeaders headers, Class<T> responseType) {
|
||||
try {
|
||||
// 构建请求头
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
if (headers != null) {
|
||||
requestHeaders.addAll(headers);
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<?> requestEntity = new HttpEntity<>(requestHeaders);
|
||||
ResponseEntity<T> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.DELETE,
|
||||
requestEntity,
|
||||
responseType
|
||||
);
|
||||
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
log.error("DELETE请求异常: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("DELETE请求异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*/
|
||||
public <T> T uploadFile(String url, File file, String fileParamName, Map<String, Object> params, Class<T> responseType) {
|
||||
try {
|
||||
// 构建表单数据
|
||||
MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
|
||||
requestBody.add(fileParamName, new FileSystemResource(file));
|
||||
if (params != null) {
|
||||
params.forEach(requestBody::add);
|
||||
}
|
||||
|
||||
// 构建请求头
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
|
||||
ResponseEntity<T> response = restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
responseType
|
||||
);
|
||||
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传异常: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("文件上传异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*/
|
||||
public ResponseEntity<String> uploadFile(String url, File file) {
|
||||
try {
|
||||
// 构建表单数据
|
||||
MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
|
||||
requestBody.add("file", new FileSystemResource(file));
|
||||
|
||||
// 构建请求头
|
||||
HttpHeaders requestHeaders = new HttpHeaders();
|
||||
requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
|
||||
return restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.POST,
|
||||
requestEntity,
|
||||
String.class
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("文件上传异常: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("文件上传异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
*/
|
||||
public byte[] downloadFile(String url) {
|
||||
try {
|
||||
ResponseEntity<byte[]> response = restTemplate.getForEntity(url, byte[].class);
|
||||
return response.getBody();
|
||||
} catch (Exception e) {
|
||||
log.error("文件下载异常: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("文件下载异常", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.njcn.http.config.RestTemplateConfig
|
||||
23
njcn-plugin/pom.xml
Normal file
23
njcn-plugin/pom.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>BasicDependVersion</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<packaging>pom</packaging>
|
||||
<artifactId>njcn-plugin</artifactId>
|
||||
<modules>
|
||||
<module>RestTemplate-plugin</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.njcn.web.utils;
|
||||
|
||||
/**
|
||||
* @author hongawen
|
||||
* @version 1.0
|
||||
* @data 2025/2/25 13:24
|
||||
*/
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
public class ThreePhaseUnbalance {
|
||||
|
||||
// 定义复数类
|
||||
static class Complex {
|
||||
BigDecimal real; // 复数的实部
|
||||
BigDecimal imag; // 复数的虚部
|
||||
|
||||
// 构造函数,初始化复数的实部和虚部
|
||||
public Complex(BigDecimal real, BigDecimal imag) {
|
||||
this.real = real;
|
||||
this.imag = imag;
|
||||
}
|
||||
|
||||
// 复数加法
|
||||
public Complex add(Complex other) {
|
||||
return new Complex(this.real.add(other.real), this.imag.add(other.imag));
|
||||
}
|
||||
|
||||
// 复数乘法
|
||||
public Complex multiply(Complex other) {
|
||||
BigDecimal real = this.real.multiply(other.real).subtract(this.imag.multiply(other.imag));
|
||||
BigDecimal imag = this.real.multiply(other.imag).add(this.imag.multiply(other.real));
|
||||
return new Complex(real, imag);
|
||||
}
|
||||
|
||||
// 复数除以一个实数
|
||||
public Complex divide(BigDecimal divisor) {
|
||||
return new Complex(this.real.divide(divisor, MathContext.DECIMAL128),
|
||||
this.imag.divide(divisor, MathContext.DECIMAL128));
|
||||
}
|
||||
|
||||
// 计算复数的模(幅值)
|
||||
public BigDecimal magnitude() {
|
||||
return BigDecimal.valueOf(Math.sqrt(real.multiply(real).add(imag.multiply(imag)).doubleValue()));
|
||||
}
|
||||
}
|
||||
|
||||
// 将幅值和角度(角度制)转换为复数
|
||||
public static Complex fromPolar(BigDecimal magnitude, BigDecimal angleDegrees) {
|
||||
double angleRadians = Math.toRadians(angleDegrees.doubleValue()); // 将角度转换为弧度
|
||||
BigDecimal real = magnitude.multiply(BigDecimal.valueOf(Math.cos(angleRadians))); // 计算实部
|
||||
BigDecimal imag = magnitude.multiply(BigDecimal.valueOf(Math.sin(angleRadians))); // 计算虚部
|
||||
return new Complex(real, imag);
|
||||
}
|
||||
|
||||
// 计算三相电压的对称分量(零序、正序、负序)
|
||||
public static Complex[] calculateSymmetricalComponents(Complex va, Complex vb, Complex vc) {
|
||||
// 120度相位因子 a = e^(j120°) = -0.5 + j * sqrt(3)/2
|
||||
Complex a = new Complex(BigDecimal.valueOf(-0.5), BigDecimal.valueOf(Math.sqrt(3) / 2));
|
||||
// 240度相位因子 a^2 = e^(j240°) = -0.5 - j * sqrt(3)/2
|
||||
Complex a2 = a.multiply(a);
|
||||
|
||||
// 零序分量 V0 = (Va + Vb + Vc) / 3
|
||||
Complex v0 = va.add(vb).add(vc).divide(BigDecimal.valueOf(3));
|
||||
|
||||
// 正序分量 V1 = (Va + a * Vb + a^2 * Vc) / 3
|
||||
Complex v1 = va.add(a.multiply(vb)).add(a2.multiply(vc)).divide(BigDecimal.valueOf(3));
|
||||
|
||||
// 负序分量 V2 = (Va + a^2 * Vb + a * Vc) / 3
|
||||
Complex v2 = va.add(a2.multiply(vb)).add(a.multiply(vc)).divide(BigDecimal.valueOf(3));
|
||||
|
||||
// 返回零序、正序、负序分量
|
||||
return new Complex[]{v0, v1, v2};
|
||||
}
|
||||
|
||||
// 计算三相电压的不平衡度
|
||||
public static void calculateUnbalance(Complex v0, Complex v1, Complex v2) {
|
||||
// 计算零序、正序、负序分量的幅值
|
||||
BigDecimal v0Mag = v0.magnitude(); // 零序分量幅值
|
||||
BigDecimal v1Mag = v1.magnitude(); // 正序分量幅值
|
||||
BigDecimal v2Mag = v2.magnitude(); // 负序分量幅值
|
||||
|
||||
// 计算不平衡度
|
||||
BigDecimal zeroSequenceUnbalance = v1Mag.compareTo(BigDecimal.ZERO) != 0 ?
|
||||
v0Mag.divide(v1Mag, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)) : BigDecimal.ZERO;
|
||||
|
||||
BigDecimal negativeSequenceUnbalance = v1Mag.compareTo(BigDecimal.ZERO) != 0 ?
|
||||
v2Mag.divide(v1Mag, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)) : BigDecimal.ZERO;
|
||||
|
||||
BigDecimal positiveSequenceUnbalance = BigDecimal.valueOf(100); // 正序电压不平衡度通常为100%
|
||||
|
||||
// 输出结果,保留2位小数
|
||||
System.out.printf("零序电压不平衡度: %.2f%%\n", zeroSequenceUnbalance.setScale(2, RoundingMode.HALF_UP).doubleValue());
|
||||
System.out.printf("负序电压不平衡度: %.2f%%\n", negativeSequenceUnbalance.setScale(2, RoundingMode.HALF_UP).doubleValue());
|
||||
System.out.printf("正序电压不平衡度: %.2f%%\n", positiveSequenceUnbalance.setScale(2, RoundingMode.HALF_UP).doubleValue());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 输入三相电压的幅值和相位角(角度制)
|
||||
System.out.println("请输入A相电压的幅值(单位:V):");
|
||||
BigDecimal vaMagnitude = new BigDecimal(57.74);
|
||||
System.out.println("请输入A相电压的相位角(单位:度):");
|
||||
BigDecimal vaAngle = new BigDecimal(0);
|
||||
|
||||
System.out.println("请输入B相电压的幅值(单位:V):");
|
||||
BigDecimal vbMagnitude =new BigDecimal(57.74*0.9);
|
||||
System.out.println("请输入B相电压的相位角(单位:度):");
|
||||
BigDecimal vbAngle = new BigDecimal(-122);
|
||||
|
||||
System.out.println("请输入C相电压的幅值(单位:V):");
|
||||
BigDecimal vcMagnitude =new BigDecimal(57.74);
|
||||
System.out.println("请输入C相电压的相位角(单位:度):");
|
||||
BigDecimal vcAngle = new BigDecimal(118);
|
||||
|
||||
// 将幅值和角度转换为复数
|
||||
Complex va = fromPolar(vaMagnitude, vaAngle);
|
||||
Complex vb = fromPolar(vbMagnitude, vbAngle);
|
||||
Complex vc = fromPolar(vcMagnitude, vcAngle);
|
||||
|
||||
// 计算对称分量
|
||||
Complex[] symmetricalComponents = calculateSymmetricalComponents(va, vb, vc);
|
||||
Complex v0 = symmetricalComponents[0]; // 零序分量
|
||||
Complex v1 = symmetricalComponents[1]; // 正序分量
|
||||
Complex v2 = symmetricalComponents[2]; // 负序分量
|
||||
|
||||
// 输出对称分量
|
||||
System.out.printf("零序分量: %.2f + j%.2f\n", v0.real.setScale(2, RoundingMode.HALF_UP).doubleValue(),
|
||||
v0.imag.setScale(2, RoundingMode.HALF_UP).doubleValue());
|
||||
System.out.printf("正序分量: %.2f + j%.2f\n", v1.real.setScale(2, RoundingMode.HALF_UP).doubleValue(),
|
||||
v1.imag.setScale(2, RoundingMode.HALF_UP).doubleValue());
|
||||
System.out.printf("负序分量: %.2f + j%.2f\n", v2.real.setScale(2, RoundingMode.HALF_UP).doubleValue(),
|
||||
v2.imag.setScale(2, RoundingMode.HALF_UP).doubleValue());
|
||||
|
||||
// 计算并输出不平衡度
|
||||
calculateUnbalance(v0, v1, v2);
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user