|
@@ -19,6 +19,7 @@ import java.nio.file.Files;
|
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Path;
|
|
|
import java.nio.file.Paths;
|
|
import java.nio.file.Paths;
|
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDate;
|
|
|
|
|
+import java.util.Locale;
|
|
|
import java.util.UUID;
|
|
import java.util.UUID;
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
|
|
|
@@ -85,16 +86,71 @@ public class UploadService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private DecodedImage decodeBase64Image(String base64Image) {
|
|
private DecodedImage decodeBase64Image(String base64Image) {
|
|
|
|
|
+ String extension = getExtensionFromBase64Image(base64Image);
|
|
|
|
|
+ String content = extractBase64Content(base64Image);
|
|
|
|
|
+ byte[] bytes = decodeBase64Content(content);
|
|
|
|
|
+ return new DecodedImage(bytes, extension);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String extractBase64Content(String base64Image) {
|
|
|
String content = base64Image.trim();
|
|
String content = base64Image.trim();
|
|
|
- String extension = "jpg";
|
|
|
|
|
- int commaIndex = content.indexOf(',');
|
|
|
|
|
- if (content.startsWith("data:") && commaIndex > -1) {
|
|
|
|
|
- String header = content.substring(0, commaIndex);
|
|
|
|
|
- extension = getExtensionFromDataUri(header);
|
|
|
|
|
- content = content.substring(commaIndex + 1);
|
|
|
|
|
|
|
+ String lowerContent = content.toLowerCase(Locale.ROOT);
|
|
|
|
|
+ int base64MarkerIndex = lowerContent.indexOf(";base64,");
|
|
|
|
|
+ if (base64MarkerIndex > -1) {
|
|
|
|
|
+ content = content.substring(base64MarkerIndex + ";base64,".length());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ int commaIndex = content.indexOf(',');
|
|
|
|
|
+ if (lowerContent.startsWith("data:") && commaIndex > -1) {
|
|
|
|
|
+ content = content.substring(commaIndex + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int startIndex = firstBase64CharIndex(content);
|
|
|
|
|
+ if (startIndex > 0) {
|
|
|
|
|
+ content = content.substring(startIndex);
|
|
|
|
|
+ }
|
|
|
|
|
+ int endIndex = firstNonBase64ContentIndex(content);
|
|
|
|
|
+ if (endIndex > -1) {
|
|
|
|
|
+ content = content.substring(0, endIndex);
|
|
|
|
|
+ }
|
|
|
|
|
+ return content;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private int firstBase64CharIndex(String content) {
|
|
|
|
|
+ for (int i = 0; i < content.length(); i++) {
|
|
|
|
|
+ if (isBase64ContentChar(content.charAt(i))) {
|
|
|
|
|
+ return i;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private int firstNonBase64ContentIndex(String content) {
|
|
|
|
|
+ for (int i = 0; i < content.length(); i++) {
|
|
|
|
|
+ if (!isBase64ContentChar(content.charAt(i))) {
|
|
|
|
|
+ return i;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return -1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean isBase64ContentChar(char ch) {
|
|
|
|
|
+ return (ch >= 'A' && ch <= 'Z')
|
|
|
|
|
+ || (ch >= 'a' && ch <= 'z')
|
|
|
|
|
+ || (ch >= '0' && ch <= '9')
|
|
|
|
|
+ || ch == '+'
|
|
|
|
|
+ || ch == '/'
|
|
|
|
|
+ || ch == '='
|
|
|
|
|
+ || Character.isWhitespace(ch);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private byte[] decodeBase64Content(String content) {
|
|
|
|
|
+ String normalizedContent = content.replaceAll("[\\r\\n\\t]", "").replace(" ", "+");
|
|
|
|
|
+ try {
|
|
|
|
|
+ return Base64.getDecoder().decode(normalizedContent);
|
|
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
|
|
+ return Base64.getDecoder().decode(content.replaceAll("\\s", ""));
|
|
|
}
|
|
}
|
|
|
- byte[] bytes = Base64.getDecoder().decode(content.replaceAll("\\s", ""));
|
|
|
|
|
- return new DecodedImage(bytes, extension);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private Path buildLocalImagePath(String licensePlate, String extension) {
|
|
private Path buildLocalImagePath(String licensePlate, String extension) {
|
|
@@ -105,21 +161,40 @@ public class UploadService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private String getExtensionFromDataUri(String header) {
|
|
private String getExtensionFromDataUri(String header) {
|
|
|
- if (header.contains("image/png")) {
|
|
|
|
|
|
|
+ String lowerHeader = header.toLowerCase(Locale.ROOT);
|
|
|
|
|
+ if (lowerHeader.contains("image/png")) {
|
|
|
return "png";
|
|
return "png";
|
|
|
}
|
|
}
|
|
|
- if (header.contains("image/gif")) {
|
|
|
|
|
|
|
+ if (lowerHeader.contains("image/gif")) {
|
|
|
return "gif";
|
|
return "gif";
|
|
|
}
|
|
}
|
|
|
- if (header.contains("image/webp")) {
|
|
|
|
|
|
|
+ if (lowerHeader.contains("image/webp")) {
|
|
|
return "webp";
|
|
return "webp";
|
|
|
}
|
|
}
|
|
|
- if (header.contains("image/bmp")) {
|
|
|
|
|
|
|
+ if (lowerHeader.contains("image/bmp")) {
|
|
|
return "bmp";
|
|
return "bmp";
|
|
|
}
|
|
}
|
|
|
return "jpg";
|
|
return "jpg";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private String getExtensionFromBase64Image(String base64Image) {
|
|
|
|
|
+ String content = base64Image.trim();
|
|
|
|
|
+ String lowerContent = content.toLowerCase(Locale.ROOT);
|
|
|
|
|
+ int base64MarkerIndex = lowerContent.indexOf(";base64,");
|
|
|
|
|
+ if (base64MarkerIndex > -1) {
|
|
|
|
|
+ int dataUriIndex = lowerContent.lastIndexOf("data:", base64MarkerIndex);
|
|
|
|
|
+ if (dataUriIndex > -1) {
|
|
|
|
|
+ return getExtensionFromDataUri(content.substring(dataUriIndex, base64MarkerIndex));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int commaIndex = content.indexOf(',');
|
|
|
|
|
+ if (lowerContent.startsWith("data:") && commaIndex > -1) {
|
|
|
|
|
+ return getExtensionFromDataUri(content.substring(0, commaIndex));
|
|
|
|
|
+ }
|
|
|
|
|
+ return "jpg";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private record DecodedImage(byte[] bytes, String extension) {
|
|
private record DecodedImage(byte[] bytes, String extension) {
|
|
|
}
|
|
}
|
|
|
|
|
|