Improve images update methods.
This commit is contained in:
		
							parent
							
								
									acc67a2077
								
							
						
					
					
						commit
						b7b889117a
					
				@ -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};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user