Improve images update methods.

This commit is contained in:
Igor Kulikov 2023-12-08 16:25:47 +02:00
parent acc67a2077
commit b7b889117a
2 changed files with 83 additions and 24 deletions

View File

@ -17,17 +17,21 @@ package org.thingsboard.server.service.install.update;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.HasImage;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.asset.AssetProfileDao;
import org.thingsboard.server.dao.dashboard.DashboardDao;
import org.thingsboard.server.dao.device.DeviceProfileDao;
import org.thingsboard.server.dao.resource.ImageService;
import org.thingsboard.server.dao.tenant.TenantDao;
import org.thingsboard.server.dao.widget.WidgetTypeDao;
import org.thingsboard.server.dao.widget.WidgetsBundleDao;
@ -41,6 +45,7 @@ public class ImagesUpdater {
private final ImageService imageService;
private final WidgetsBundleDao widgetsBundleDao;
private final WidgetTypeDao widgetTypeDao;
private final TenantDao tenantDao;
private final DashboardDao dashboardDao;
private final DeviceProfileDao deviceProfileDao;
private final AssetProfileDao assetProfileDao;
@ -59,8 +64,7 @@ public class ImagesUpdater {
public void updateDashboardsImages() {
log.info("Updating dashboards images...");
var dashboardsIds = new PageDataIterable<>(dashboardDao::findAllIds, 1024);
updateImages(dashboardsIds, "dashboard", imageService::replaceBase64WithImageUrl, dashboardDao);
updateImages("dashboard", dashboardDao::findIdsByTenantId, imageService::replaceBase64WithImageUrl, dashboardDao);
}
public void createSystemImages(Dashboard defaultDashboard) {
@ -108,11 +112,44 @@ public class ImagesUpdater {
private <E extends HasImage> void updateImages(Iterable<? extends EntityId> entitiesIds, String type,
Function<E, Boolean> updater, Dao<E> dao) {
int updatedCount = 0;
int totalCount = 0;
int updatedCount = 0;
var counts = updateImages(entitiesIds, type, updater, dao, totalCount, updatedCount);
totalCount = counts[0];
updatedCount = counts[1];
log.info("Updated {} {}s out of {}", updatedCount, type, totalCount);
}
private <E extends HasImage> void updateImages(String type, BiFunction<TenantId, PageLink, PageData<? extends EntityId>> entityIdsByTenantId,
Function<E, Boolean> updater, Dao<E> dao) {
int tenantCount = 0;
int totalCount = 0;
int updatedCount = 0;
var tenantIds = new PageDataIterable<>(tenantDao::findTenantsIds, 128);
for (var tenantId : tenantIds) {
tenantCount++;
var entitiesIds = new PageDataIterable<>(link -> entityIdsByTenantId.apply(tenantId, link), 128);
var counts = updateImages(entitiesIds, type, updater, dao, totalCount, updatedCount);
totalCount = counts[0];
updatedCount = counts[1];
if (tenantCount % 100 == 0) {
log.info("Update {}s images: processed {} tenants so far", type, tenantCount);
}
}
log.info("Updated {} {}s out of {}", updatedCount, type, totalCount);
}
private <E extends HasImage> int[] updateImages(Iterable<? extends EntityId> entitiesIds, String type,
Function<E, Boolean> updater, Dao<E> dao, int totalCount, int updatedCount) {
for (EntityId id : entitiesIds) {
totalCount++;
E entity = dao.findById(TenantId.SYS_TENANT_ID, id.getId());
E entity;
try {
entity = dao.findById(TenantId.SYS_TENANT_ID, id.getId());
} catch (Exception e) {
log.error("Failed to update {} images: error fetching {} by id [{}]: {}", type, type, id.getId(), StringUtils.abbreviate(e.toString(), 1000));
continue;
}
try {
boolean updated = updater.apply(entity);
if (updated) {
@ -127,7 +164,7 @@ public class ImagesUpdater {
log.info("Processed {} {}s so far", totalCount, type);
}
}
log.info("Updated {} {}s out of {}", updatedCount, type, totalCount);
return new int[]{totalCount, updatedCount};
}
}

View File

@ -34,6 +34,7 @@ import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.batik.util.XMLResourceDescriptor;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import org.thingsboard.server.common.data.StringUtils;
@ -69,13 +70,17 @@ public class ImageUtils {
public static ProcessedImage processImage(byte[] data, String mediaType, int thumbnailMaxDimension) throws Exception {
if (mediaTypeToFileExtension(mediaType).equals("svg")) {
return processSvgImage(data, mediaType, thumbnailMaxDimension);
try {
return processSvgImage(data, mediaType, thumbnailMaxDimension);
} catch (Exception e) {
if (log.isDebugEnabled()) { // printing stacktrace
log.warn("Couldn't process SVG image, leaving preview as original image", e);
} else {
log.warn("Couldn't process SVG image, leaving preview as original image: {}", ExceptionUtils.getMessage(e));
}
return previewAsOriginalImage(data, mediaType);
}
}
ProcessedImage image = new ProcessedImage();
image.setMediaType(mediaType);
image.setData(data);
image.setSize(data.length);
BufferedImage bufferedImage = null;
try {
bufferedImage = ImageIO.read(new ByteArrayInputStream(data));
@ -83,6 +88,8 @@ public class ImageUtils {
}
if (bufferedImage == null) { // means that media type is not supported by ImageIO; extracting width and height from metadata and leaving preview as original image
Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(data));
ProcessedImage image = previewAsOriginalImage(data, mediaType);
String dirName = "Unknown";
for (Directory dir : metadata.getDirectories()) {
Tag widthTag = dir.getTags().stream()
.filter(tag -> tag.getTagName().toLowerCase().contains("width"))
@ -94,24 +101,22 @@ public class ImageUtils {
continue;
}
int width = Integer.parseInt(dir.getObject(widthTag.getTagType()).toString());
int height = Integer.parseInt(dir.getObject(widthTag.getTagType()).toString());
int height = Integer.parseInt(dir.getObject(heightTag.getTagType()).toString());
image.setWidth(width);
image.setHeight(height);
ProcessedImage preview = new ProcessedImage();
preview.setWidth(image.getWidth());
preview.setHeight(image.getHeight());
preview.setMediaType(mediaType);
preview.setData(null);
preview.setSize(data.length);
image.setPreview(preview);
log.warn("Couldn't process {} ({}) with ImageIO, leaving preview as original image", mediaType, dir.getName());
return image;
image.getPreview().setWidth(width);
image.getPreview().setHeight(height);
dirName = dir.getName();
break;
}
log.warn("Image media type {} not supported", mediaType);
throw new IllegalArgumentException("Media type " + mediaType + " not supported");
log.warn("Couldn't process {} ({}) with ImageIO, leaving preview as original image", mediaType, dirName);
return image;
}
ProcessedImage image = new ProcessedImage();
image.setMediaType(mediaType);
image.setData(data);
image.setSize(data.length);
image.setWidth(bufferedImage.getWidth());
image.setHeight(bufferedImage.getHeight());
@ -202,6 +207,23 @@ public class ImageUtils {
return image;
}
private static ProcessedImage previewAsOriginalImage(byte[] data, String mediaType) {
ProcessedImage image = new ProcessedImage();
image.setMediaType(mediaType);
image.setData(data);
image.setSize(data.length);
image.setWidth(0);
image.setHeight(0);
ProcessedImage preview = new ProcessedImage();
preview.setMediaType(mediaType);
preview.setData(null);
preview.setSize(data.length);
preview.setWidth(0);
preview.setHeight(0);
image.setPreview(preview);
return image;
}
private static int[] getThumbnailDimensions(int originalWidth, int originalHeight, int maxDimension) {
if (originalWidth <= maxDimension && originalHeight <= maxDimension) {
return new int[]{originalWidth, originalHeight};