Remove tenantId from image link; remove link from resource
This commit is contained in:
parent
d06cfa9ae0
commit
7489b87fa5
@ -28,6 +28,5 @@ $$
|
||||
END;
|
||||
$$;
|
||||
ALTER TABLE resource ADD COLUMN IF NOT EXISTS media_type varchar(255);
|
||||
ALTER TABLE resource ADD COLUMN IF NOT EXISTS link varchar(255);
|
||||
|
||||
-- RESOURCES UPDATE END
|
||||
|
||||
@ -35,7 +35,6 @@ import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thingsboard.server.common.data.ResourceType;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
@ -93,7 +92,6 @@ public class TbResourceController extends BaseController {
|
||||
@ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
@GetMapping(value = "/resource/{resourceId}/download")
|
||||
@ResponseBody
|
||||
public ResponseEntity<ByteArrayResource> downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException {
|
||||
checkParameter(RESOURCE_ID, strResourceId);
|
||||
@ -111,17 +109,10 @@ public class TbResourceController extends BaseController {
|
||||
|
||||
@ApiOperation(value = "Download Image (downloadImageIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@GetMapping(value = "/images/{tenantId}/{resourceKey}", produces = "image/*")
|
||||
@ResponseBody
|
||||
public ResponseEntity<ByteArrayResource> downloadImageIfChanged(@PathVariable("tenantId") String tenantIdStr,
|
||||
@PathVariable("resourceKey") String resourceKey,
|
||||
@GetMapping(value = "/images/{resourceKey}", produces = "image/*")
|
||||
public ResponseEntity<ByteArrayResource> downloadImageIfChanged(@PathVariable("resourceKey") String resourceKey,
|
||||
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException {
|
||||
TenantId tenantId;
|
||||
if (tenantIdStr.equals("system")) {
|
||||
tenantId = TenantId.SYS_TENANT_ID;
|
||||
} else {
|
||||
tenantId = TenantId.fromUUID(toUUID(tenantIdStr));
|
||||
}
|
||||
TenantId tenantId = getTenantId();
|
||||
return downloadResourceIfChanged(ResourceType.IMAGE, etag,
|
||||
() -> resourceService.findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, resourceKey),
|
||||
() -> resourceService.findResourceByTenantIdAndKey(tenantId, ResourceType.IMAGE, resourceKey));
|
||||
@ -130,7 +121,6 @@ public class TbResourceController extends BaseController {
|
||||
@ApiOperation(value = "Download LWM2M Resource (downloadLwm2mResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
@GetMapping(value = "/resource/lwm2m/{resourceId}/download", produces = "application/xml")
|
||||
@ResponseBody
|
||||
public ResponseEntity<ByteArrayResource> downloadLwm2mResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RESOURCE_ID) String strResourceId,
|
||||
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException {
|
||||
@ -140,7 +130,6 @@ public class TbResourceController extends BaseController {
|
||||
@ApiOperation(value = "Download PKCS_12 Resource (downloadPkcs12ResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/resource/pkcs12/{resourceId}/download", method = RequestMethod.GET, produces = "application/x-pkcs12")
|
||||
@ResponseBody
|
||||
public ResponseEntity<ByteArrayResource> downloadPkcs12ResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RESOURCE_ID) String strResourceId,
|
||||
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException {
|
||||
@ -151,7 +140,6 @@ public class TbResourceController extends BaseController {
|
||||
notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
@GetMapping(value = "/resource/jks/{resourceId}/download", produces = "application/x-java-keystore")
|
||||
@ResponseBody
|
||||
public ResponseEntity<ByteArrayResource> downloadJksResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RESOURCE_ID) String strResourceId,
|
||||
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException {
|
||||
@ -161,7 +149,6 @@ public class TbResourceController extends BaseController {
|
||||
@ApiOperation(value = "Download JS Resource (downloadJsResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
@GetMapping(value = "/resource/js/{resourceId}/download", produces = "application/javascript")
|
||||
@ResponseBody
|
||||
public ResponseEntity<ByteArrayResource> downloadJsResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RESOURCE_ID) String strResourceId,
|
||||
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException {
|
||||
@ -174,7 +161,6 @@ public class TbResourceController extends BaseController {
|
||||
produces = "application/json")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
@GetMapping(value = "/resource/info/{resourceId}")
|
||||
@ResponseBody
|
||||
public TbResourceInfo getResourceInfoById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException {
|
||||
checkParameter(RESOURCE_ID, strResourceId);
|
||||
@ -189,7 +175,6 @@ public class TbResourceController extends BaseController {
|
||||
@Deprecated // resource's data should be fetched with a download request
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
@GetMapping(value = "/resource/{resourceId}")
|
||||
@ResponseBody
|
||||
public TbResource getResourceById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException {
|
||||
checkParameter(RESOURCE_ID, strResourceId);
|
||||
@ -211,7 +196,6 @@ public class TbResourceController extends BaseController {
|
||||
consumes = "application/json")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
@PostMapping(value = "/resource")
|
||||
@ResponseBody
|
||||
public TbResourceInfo saveResource(@ApiParam(value = "A JSON value representing the Resource.")
|
||||
@RequestBody TbResource resource) throws Exception {
|
||||
resource.setTenantId(getTenantId());
|
||||
@ -225,7 +209,6 @@ public class TbResourceController extends BaseController {
|
||||
produces = "application/json")
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
@GetMapping(value = "/resource")
|
||||
@ResponseBody
|
||||
public PageData<TbResourceInfo> getResources(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
|
||||
@RequestParam int pageSize,
|
||||
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
|
||||
@ -257,7 +240,6 @@ public class TbResourceController extends BaseController {
|
||||
produces = "application/json")
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@GetMapping(value = "/resource/lwm2m/page")
|
||||
@ResponseBody
|
||||
public List<LwM2mObject> getLwm2mListObjectsPage(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
|
||||
@RequestParam int pageSize,
|
||||
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
|
||||
@ -278,7 +260,6 @@ public class TbResourceController extends BaseController {
|
||||
produces = "application/json")
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@GetMapping(value = "/resource/lwm2m")
|
||||
@ResponseBody
|
||||
public List<LwM2mObject> getLwm2mListObjects(@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES, required = true)
|
||||
@RequestParam String sortOrder,
|
||||
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = LWM2M_OBJECT_SORT_PROPERTY_ALLOWABLE_VALUES, required = true)
|
||||
@ -292,7 +273,6 @@ public class TbResourceController extends BaseController {
|
||||
notes = "Deletes the Resource. Referencing non-existing Resource Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
|
||||
@DeleteMapping(value = "/resource/{resourceId}")
|
||||
@ResponseBody
|
||||
public void deleteResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable("resourceId") String strResourceId) throws ThingsboardException {
|
||||
checkParameter(RESOURCE_ID, strResourceId);
|
||||
@ -323,6 +303,7 @@ public class TbResourceController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: rate limits
|
||||
TbResource tbResource = resourceSupplier.get();
|
||||
checkEntity(getCurrentUser(), tbResource, Operation.READ);
|
||||
|
||||
@ -336,4 +317,5 @@ public class TbResourceController extends BaseController {
|
||||
.eTag(tbResource.getEtag())
|
||||
.body(resource);
|
||||
}
|
||||
|
||||
}
|
||||
@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.ResourceType;
|
||||
import org.thingsboard.server.common.data.TbResource;
|
||||
import org.thingsboard.server.common.data.TbResourceInfo;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.util.MediaTypeUtils;
|
||||
import org.thingsboard.server.common.data.util.ImageUtils;
|
||||
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
|
||||
import org.thingsboard.server.common.data.widget.WidgetsBundle;
|
||||
import org.thingsboard.server.dao.resource.ResourceService;
|
||||
@ -65,7 +65,7 @@ public class ImagesUpdater {
|
||||
throw new IllegalArgumentException("Image name is missing for " + imageKey + ". Please add it to names.json file");
|
||||
}
|
||||
byte[] imageData = Files.readAllBytes(imageFile);
|
||||
String mediaType = MediaTypeUtils.fileExtensionToMediaType("image", StringUtils.substringAfterLast(imageKey, "."));
|
||||
String mediaType = ImageUtils.fileExtensionToMediaType("image", StringUtils.substringAfterLast(imageKey, "."));
|
||||
try {
|
||||
saveImage(TenantId.SYS_TENANT_ID, imageName, imageKey, imageData, mediaType, null);
|
||||
} catch (Exception e) {
|
||||
@ -191,7 +191,7 @@ public class ImagesUpdater {
|
||||
}
|
||||
|
||||
String imageMediaType = StringUtils.substringBetween(data, "data:", ";base64");
|
||||
String extension = MediaTypeUtils.mediaTypeToFileExtension(imageMediaType);
|
||||
String extension = ImageUtils.mediaTypeToFileExtension(imageMediaType);
|
||||
key += "." + extension;
|
||||
|
||||
byte[] imageData = Base64.getDecoder().decode(base64Data);
|
||||
@ -203,14 +203,16 @@ public class ImagesUpdater {
|
||||
String existingImageQuery) {
|
||||
TbResourceInfo resourceInfo = resourceService.findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, key);
|
||||
if (resourceInfo == null && !tenantId.isSysTenantId() && existingImageQuery != null) {
|
||||
// TODO: need to search among tenant images too (custom widgets)
|
||||
List<TbResourceInfo> existingSystemImages = resourceService.findByTenantIdAndDataAndKeyStartingWith(TenantId.SYS_TENANT_ID, imageData, existingImageQuery);
|
||||
if (!existingSystemImages.isEmpty()) {
|
||||
resourceInfo = existingSystemImages.get(0);
|
||||
if (existingSystemImages.size() > 1) {
|
||||
log.warn("Found more than one system image resources for key {}", existingImageQuery);
|
||||
}
|
||||
log.info("Using system image {} for {}", resourceInfo.getLink(), key);
|
||||
return resourceInfo.getLink();
|
||||
String link = resourceService.getResourceLink(resourceInfo);
|
||||
log.info("Using system image {} for {}", link, key);
|
||||
return link;
|
||||
}
|
||||
}
|
||||
TbResource resource;
|
||||
@ -222,7 +224,7 @@ public class ImagesUpdater {
|
||||
} else if (tenantId.isSysTenantId()) {
|
||||
resource = new TbResource(resourceInfo);
|
||||
} else {
|
||||
return resourceInfo.getLink();
|
||||
return resourceService.getResourceLink(resourceInfo);
|
||||
}
|
||||
resource.setTitle(name);
|
||||
resource.setFileName(key);
|
||||
@ -231,7 +233,7 @@ public class ImagesUpdater {
|
||||
resource = resourceService.saveResource(resource);
|
||||
log.info("[{}] {} image '{}' ({})", tenantId, resourceInfo == null ? "Created" : "Updated",
|
||||
resource.getTitle(), resource.getResourceKey());
|
||||
return resource.getLink();
|
||||
return resourceService.getResourceLink(resourceInfo);
|
||||
}
|
||||
|
||||
private String getText(JsonNode jsonNode, String field) {
|
||||
|
||||
@ -676,10 +676,9 @@ public class TbResourceControllerTest extends AbstractControllerTest {
|
||||
resource.setBase64Data(TEST_DATA);
|
||||
|
||||
TbResource savedResource = save(resource);
|
||||
assertThat(savedResource.getLink()).isEqualTo("/api/images/system/image.png");
|
||||
|
||||
loginTenantAdmin();
|
||||
MockHttpServletResponse imageResponse = doGet(savedResource.getLink()).andExpect(status().isOk())
|
||||
MockHttpServletResponse imageResponse = doGet(getImageLink(savedResource)).andExpect(status().isOk())
|
||||
.andReturn().getResponse();
|
||||
assertThat(imageResponse.getContentAsByteArray())
|
||||
.isEqualTo(download(savedResource.getId()))
|
||||
@ -701,9 +700,9 @@ public class TbResourceControllerTest extends AbstractControllerTest {
|
||||
resource.setBase64Data(TEST_DATA);
|
||||
|
||||
TbResource savedResource = save(resource);
|
||||
assertThat(savedResource.getLink()).isEqualTo("/api/images/" + tenantId + "/image.jpg");
|
||||
String imageLink = getImageLink(savedResource);
|
||||
|
||||
MockHttpServletResponse imageResponse = doGet(savedResource.getLink()).andExpect(status().isOk())
|
||||
MockHttpServletResponse imageResponse = doGet(imageLink).andExpect(status().isOk())
|
||||
.andReturn().getResponse();
|
||||
assertThat(imageResponse.getContentAsByteArray())
|
||||
.isEqualTo(download(savedResource.getId()))
|
||||
@ -711,7 +710,7 @@ public class TbResourceControllerTest extends AbstractControllerTest {
|
||||
assertThat(imageResponse.getContentType()).isEqualTo("image/jpeg");
|
||||
|
||||
loginDifferentTenant();
|
||||
doGet(savedResource.getLink()).andExpect(status().isForbidden());
|
||||
doGet(imageLink).andExpect(status().isNotFound());
|
||||
}
|
||||
|
||||
private TbResource save(TbResource tbResource) throws Exception {
|
||||
@ -724,4 +723,8 @@ public class TbResourceControllerTest extends AbstractControllerTest {
|
||||
.andExpect(status().isOk())
|
||||
.andReturn().getResponse().getContentAsByteArray();
|
||||
}
|
||||
|
||||
private String getImageLink(TbResourceInfo resourceInfo) {
|
||||
return "/api/images/" + (resourceInfo.getTenantId().isSysTenantId() ? "system/" : "") + resourceInfo.getResourceKey();
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,4 +62,6 @@ public interface ResourceService extends EntityDaoService {
|
||||
|
||||
List<TbResourceInfo> findByTenantIdAndDataAndKeyStartingWith(TenantId tenantId, byte[] data, String query);
|
||||
|
||||
String getResourceLink(TbResourceInfo resourceInfo);
|
||||
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
package org.thingsboard.server.common.data;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
@ -56,8 +57,7 @@ public class TbResourceInfo extends BaseData<TbResourceId> implements HasName, H
|
||||
private String fileName;
|
||||
@ApiModelProperty(position = 10, value = "Resource media type.", example = "image/png", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
||||
private String mediaType;
|
||||
@ApiModelProperty(position = 11, value = "Resource link (for IMAGE resource type).", example = "/api/images/system/my-image.png", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
||||
private String link;
|
||||
private ObjectNode descriptor;
|
||||
|
||||
public TbResourceInfo() {
|
||||
super();
|
||||
@ -77,7 +77,7 @@ public class TbResourceInfo extends BaseData<TbResourceId> implements HasName, H
|
||||
this.etag = resourceInfo.etag;
|
||||
this.fileName = resourceInfo.fileName;
|
||||
this.mediaType = resourceInfo.mediaType;
|
||||
this.link = resourceInfo.link;
|
||||
this.descriptor = resourceInfo.descriptor;
|
||||
}
|
||||
|
||||
@ApiModelProperty(position = 1, value = "JSON object with the Resource Id. " +
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data.util;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ImageUtils {
|
||||
|
||||
private static final Map<String, String> mediaTypeMappings = Map.of(
|
||||
"jpeg", "jpg",
|
||||
"svg+xml", "svg"
|
||||
);
|
||||
|
||||
public static String mediaTypeToFileExtension(String mimeType) {
|
||||
String subtype = MimeTypeUtils.parseMimeType(mimeType).getSubtype();
|
||||
return mediaTypeMappings.getOrDefault(subtype, subtype);
|
||||
}
|
||||
|
||||
public static String fileExtensionToMediaType(String type, String extension) {
|
||||
String subtype = mediaTypeMappings.entrySet().stream()
|
||||
.filter(mapping -> mapping.getValue().equals(extension))
|
||||
.map(Map.Entry::getKey).findFirst().orElse(extension);
|
||||
return new MimeType(type, subtype).toString();
|
||||
}
|
||||
|
||||
public static ImageInfo processImage(byte[] imageData) throws IOException {
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageData));
|
||||
ImageThumbnail thumbnail = getImageThumbnail(image, 250);
|
||||
return new ImageInfo(image.getWidth(), image.getHeight(), thumbnail);
|
||||
}
|
||||
|
||||
private static ImageThumbnail getImageThumbnail(BufferedImage originalImage, int maxDimension) throws IOException {
|
||||
int originalWidth = originalImage.getWidth();
|
||||
int originalHeight = originalImage.getHeight();
|
||||
int thumbnailWidth;
|
||||
int thumbnailHeight;
|
||||
if (originalWidth <= maxDimension && originalHeight <= maxDimension) {
|
||||
thumbnailWidth = originalWidth;
|
||||
thumbnailHeight = originalHeight;
|
||||
} else {
|
||||
double aspectRatio = (double) originalWidth / originalHeight;
|
||||
if (originalWidth > originalHeight) {
|
||||
thumbnailWidth = maxDimension;
|
||||
thumbnailHeight = (int) (maxDimension / aspectRatio);
|
||||
} else {
|
||||
thumbnailWidth = (int) (maxDimension * aspectRatio);
|
||||
thumbnailHeight = maxDimension;
|
||||
}
|
||||
}
|
||||
BufferedImage thumbnail = new BufferedImage(thumbnailWidth, thumbnailHeight, BufferedImage.TYPE_INT_RGB);
|
||||
thumbnail.getGraphics().drawImage(originalImage, 0, 0, thumbnailWidth, thumbnailHeight, null);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
ImageIO.write(thumbnail, "ignored", os);
|
||||
return new ImageThumbnail(thumbnail.getWidth(), thumbnail.getHeight(), os.toByteArray());
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ImageInfo {
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final ImageThumbnail thumbnail;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ImageThumbnail {
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final byte[] data;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright © 2016-2023 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.data.util;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class MediaTypeUtils {
|
||||
|
||||
private static final Map<String, String> mappings = Map.of(
|
||||
"jpeg", "jpg",
|
||||
"svg+xml", "svg"
|
||||
);
|
||||
|
||||
public static String mediaTypeToFileExtension(String mimeType) {
|
||||
String subtype = MimeTypeUtils.parseMimeType(mimeType).getSubtype();
|
||||
return mappings.getOrDefault(subtype, subtype);
|
||||
}
|
||||
|
||||
public static String fileExtensionToMediaType(String type, String extension) {
|
||||
String subtype = mappings.entrySet().stream()
|
||||
.filter(mapping -> mapping.getValue().equals(extension))
|
||||
.map(Map.Entry::getKey).findFirst().orElse(extension);
|
||||
return new MimeType(type, subtype).toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -500,7 +500,6 @@ public class ModelConstants {
|
||||
public static final String RESOURCE_DATA_COLUMN = "data";
|
||||
public static final String RESOURCE_ETAG_COLUMN = "etag";
|
||||
public static final String RESOURCE_MEDIA_TYPE_COLUMN = "media_type";
|
||||
public static final String RESOURCE_LINK_COLUMN = "link";
|
||||
|
||||
/**
|
||||
* Ota Package constants.
|
||||
|
||||
@ -33,7 +33,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLU
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_LINK_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_MEDIA_TYPE_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN;
|
||||
@ -74,9 +73,6 @@ public class TbResourceEntity extends BaseSqlEntity<TbResource> implements BaseE
|
||||
@Column(name = RESOURCE_MEDIA_TYPE_COLUMN)
|
||||
private String mediaType;
|
||||
|
||||
@Column(name = RESOURCE_LINK_COLUMN)
|
||||
private String link;
|
||||
|
||||
public TbResourceEntity() {
|
||||
}
|
||||
|
||||
@ -96,7 +92,6 @@ public class TbResourceEntity extends BaseSqlEntity<TbResource> implements BaseE
|
||||
this.data = resource.getData();
|
||||
this.etag = resource.getEtag();
|
||||
this.mediaType = resource.getMediaType();
|
||||
this.link = resource.getLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -112,7 +107,6 @@ public class TbResourceEntity extends BaseSqlEntity<TbResource> implements BaseE
|
||||
resource.setData(data);
|
||||
resource.setEtag(etag);
|
||||
resource.setMediaType(mediaType);
|
||||
resource.setLink(link);
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ import java.util.UUID;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_LINK_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_MEDIA_TYPE_COLUMN;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME;
|
||||
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN;
|
||||
@ -70,9 +69,6 @@ public class TbResourceInfoEntity extends BaseSqlEntity<TbResourceInfo> implemen
|
||||
@Column(name = RESOURCE_MEDIA_TYPE_COLUMN)
|
||||
private String mediaType;
|
||||
|
||||
@Column(name = RESOURCE_LINK_COLUMN)
|
||||
private String link;
|
||||
|
||||
public TbResourceInfoEntity() {
|
||||
}
|
||||
|
||||
@ -89,7 +85,6 @@ public class TbResourceInfoEntity extends BaseSqlEntity<TbResourceInfo> implemen
|
||||
this.hashCode = resource.getEtag();
|
||||
this.fileName = resource.getFileName();
|
||||
this.mediaType = resource.getMediaType();
|
||||
this.link = resource.getLink();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -104,7 +99,6 @@ public class TbResourceInfoEntity extends BaseSqlEntity<TbResourceInfo> implemen
|
||||
resource.setEtag(hashCode);
|
||||
resource.setFileName(fileName);
|
||||
resource.setMediaType(mediaType);
|
||||
resource.setLink(link);
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,11 +77,10 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
|
||||
}
|
||||
if (resource.getData() != null) {
|
||||
resource.setEtag(calculateEtag(resource.getData()));
|
||||
}
|
||||
if (resource.getResourceType() == ResourceType.IMAGE) {
|
||||
resource.setLink(String.format("/api/images/%s/%s",
|
||||
tenantId.isSysTenantId() ? "system" : tenantId.getId(),
|
||||
resource.getResourceKey()));
|
||||
// FIXME: skip SVG (?)
|
||||
|
||||
}
|
||||
}
|
||||
try {
|
||||
TbResource saved;
|
||||
@ -226,6 +225,16 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
|
||||
return resourceInfoDao.findByTenantIdAndEtagAndKeyStartingWith(tenantId, etag, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceLink(TbResourceInfo resourceInfo) {
|
||||
String link = "/api/images/";
|
||||
if (resourceInfo.getTenantId().isSysTenantId()) {
|
||||
link += "system/";
|
||||
}
|
||||
link += resourceInfo.getResourceKey();
|
||||
return link;
|
||||
}
|
||||
|
||||
private String calculateEtag(byte[] data) {
|
||||
return Hashing.sha256().hashBytes(data).toString();
|
||||
}
|
||||
|
||||
@ -717,7 +717,6 @@ CREATE TABLE IF NOT EXISTS resource (
|
||||
data bytea,
|
||||
etag varchar,
|
||||
media_type varchar(255),
|
||||
link varchar(255),
|
||||
CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key)
|
||||
);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user