Browse Source

Merge branch 'feature/public-images' of github.com:thingsboard/thingsboard into feature/public-images

pull/9846/head
Igor Kulikov 3 years ago
parent
commit
c4d8be8549
  1. 39
      application/src/main/java/org/thingsboard/server/controller/ImageController.java
  2. 75
      application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java
  3. 39
      dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java

39
application/src/main/java/org/thingsboard/server/controller/ImageController.java

@ -89,6 +89,10 @@ public class ImageController extends BaseController {
private static final String SYSTEM_IMAGE = "system"; private static final String SYSTEM_IMAGE = "system";
private static final String TENANT_IMAGE = "tenant"; private static final String TENANT_IMAGE = "tenant";
private static final String IMAGE_TYPE_PARAM_DESCRIPTION = "Type of the image: tenant or system";
private static final String IMAGE_TYPE_PARAM_ALLOWABLE_VALUES = "tenant, system";
private static final String IMAGE_KEY_PARAM_DESCRIPTION = "Image resource key, for example thermostats_dashboard_background.jpeg";
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PostMapping("/api/image") @PostMapping("/api/image")
public TbResourceInfo uploadImage(@RequestPart MultipartFile file, public TbResourceInfo uploadImage(@RequestPart MultipartFile file,
@ -117,7 +121,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PutMapping(IMAGE_URL) @PutMapping(IMAGE_URL)
public TbResourceInfo updateImage(@PathVariable String type, public TbResourceInfo updateImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
@PathVariable String type,
@ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key, @PathVariable String key,
@RequestPart MultipartFile file) throws Exception { @RequestPart MultipartFile file) throws Exception {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE);
@ -135,7 +141,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PutMapping(IMAGE_URL + "/info") @PutMapping(IMAGE_URL + "/info")
public TbResourceInfo updateImageInfo(@PathVariable String type, public TbResourceInfo updateImageInfo(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
@PathVariable String type,
@ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key, @PathVariable String key,
@RequestBody TbResourceInfo newImageInfo) throws ThingsboardException { @RequestBody TbResourceInfo newImageInfo) throws ThingsboardException {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE);
@ -145,7 +153,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PutMapping(IMAGE_URL + "/public/{isPublic}") @PutMapping(IMAGE_URL + "/public/{isPublic}")
public TbResourceInfo updateImagePublicStatus(@PathVariable String type, public TbResourceInfo updateImagePublicStatus(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
@PathVariable String type,
@ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key, @PathVariable String key,
@PathVariable boolean isPublic) throws ThingsboardException { @PathVariable boolean isPublic) throws ThingsboardException {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE);
@ -155,7 +165,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = IMAGE_URL, produces = "image/*") @GetMapping(value = IMAGE_URL, produces = "image/*")
public ResponseEntity<ByteArrayResource> downloadImage(@PathVariable String type, public ResponseEntity<ByteArrayResource> downloadImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
@PathVariable String type,
@ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key, @PathVariable String key,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
return downloadIfChanged(type, key, etag, false); return downloadIfChanged(type, key, etag, false);
@ -170,7 +182,10 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = IMAGE_URL + "/export") @GetMapping(value = IMAGE_URL + "/export")
public ImageExportData exportImage(@PathVariable String type, @PathVariable String key) throws Exception { public ImageExportData exportImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
@PathVariable String type,
@ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key) throws Exception {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ);
ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class);
byte[] data = imageService.getImageData(imageInfo.getTenantId(), imageInfo.getId()); byte[] data = imageService.getImageData(imageInfo.getTenantId(), imageInfo.getId());
@ -212,7 +227,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = IMAGE_URL + "/preview", produces = "image/png") @GetMapping(value = IMAGE_URL + "/preview", produces = "image/png")
public ResponseEntity<ByteArrayResource> downloadImagePreview(@PathVariable String type, public ResponseEntity<ByteArrayResource> downloadImagePreview(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
@PathVariable String type,
@ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key, @PathVariable String key,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
return downloadIfChanged(type, key, etag, true); return downloadIfChanged(type, key, etag, true);
@ -220,7 +237,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(IMAGE_URL + "/info") @GetMapping(IMAGE_URL + "/info")
public TbResourceInfo getImageInfo(@PathVariable String type, public TbResourceInfo getImageInfo(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
@PathVariable String type,
@ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key) throws ThingsboardException { @PathVariable String key) throws ThingsboardException {
return checkImageInfo(type, key, Operation.READ); return checkImageInfo(type, key, Operation.READ);
} }
@ -251,7 +270,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@DeleteMapping(IMAGE_URL) @DeleteMapping(IMAGE_URL)
public ResponseEntity<TbImageDeleteResult> deleteImage(@PathVariable String type, public ResponseEntity<TbImageDeleteResult> deleteImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
@PathVariable String type,
@ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key, @PathVariable String key,
@RequestParam(name = "force", required = false) boolean force) throws ThingsboardException { @RequestParam(name = "force", required = false) boolean force) throws ThingsboardException {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.DELETE); TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.DELETE);
@ -272,7 +293,7 @@ public class ImageController extends BaseController {
} }
} }
TbResourceInfo imageInfo = imageInfoSupplier.get(); TbResourceInfo imageInfo = checkNotNull(imageInfoSupplier.get());
String fileName = imageInfo.getFileName(); String fileName = imageInfo.getFileName();
ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class);
byte[] data; byte[] data;

75
application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java

@ -22,8 +22,9 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.mock.web.MockPart;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.ImageDescriptor; import org.thingsboard.server.common.data.ImageDescriptor;
@ -64,7 +65,7 @@ public class ImageControllerTest extends AbstractControllerTest {
@Test @Test
public void testUploadPngImage() throws Exception { public void testUploadPngImage() throws Exception {
String filename = "my_png_image.png"; String filename = "my_png_image.png";
TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE); TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false);
assertThat(imageInfo.getTitle()).isEqualTo(filename); assertThat(imageInfo.getTitle()).isEqualTo(filename);
assertThat(imageInfo.getResourceType()).isEqualTo(ResourceType.IMAGE); assertThat(imageInfo.getResourceType()).isEqualTo(ResourceType.IMAGE);
@ -83,7 +84,7 @@ public class ImageControllerTest extends AbstractControllerTest {
@Test @Test
public void testUploadJpegImage() throws Exception { public void testUploadJpegImage() throws Exception {
String filename = "my_jpeg_image.jpg"; String filename = "my_jpeg_image.jpg";
TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE); TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE, false);
ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class); ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class);
checkJpegImageDescriptor(imageDescriptor); checkJpegImageDescriptor(imageDescriptor);
@ -95,7 +96,7 @@ public class ImageControllerTest extends AbstractControllerTest {
@Test @Test
public void testUploadSvgImage() throws Exception { public void testUploadSvgImage() throws Exception {
String filename = "my_svg_image.svg"; String filename = "my_svg_image.svg";
TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/svg+xml", SVG_IMAGE); TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/svg+xml", SVG_IMAGE, false);
ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class); ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class);
checkSvgImageDescriptor(imageDescriptor); checkSvgImageDescriptor(imageDescriptor);
@ -107,17 +108,17 @@ public class ImageControllerTest extends AbstractControllerTest {
@Test @Test
public void testUploadImageWithSameFilename() throws Exception { public void testUploadImageWithSameFilename() throws Exception {
String filename = "my_jpeg_image.jpg"; String filename = "my_jpeg_image.jpg";
TbResourceInfo imageInfo1 = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE); TbResourceInfo imageInfo1 = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE, false);
assertThat(imageInfo1.getTitle()).isEqualTo(filename); assertThat(imageInfo1.getTitle()).isEqualTo(filename);
assertThat(imageInfo1.getFileName()).isEqualTo(filename); assertThat(imageInfo1.getFileName()).isEqualTo(filename);
assertThat(imageInfo1.getResourceKey()).isEqualTo(filename); assertThat(imageInfo1.getResourceKey()).isEqualTo(filename);
TbResourceInfo imageInfo2 = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE); TbResourceInfo imageInfo2 = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE, false);
assertThat(imageInfo2.getTitle()).isEqualTo(filename); assertThat(imageInfo2.getTitle()).isEqualTo(filename);
assertThat(imageInfo2.getFileName()).isEqualTo(filename); assertThat(imageInfo2.getFileName()).isEqualTo(filename);
assertThat(imageInfo2.getResourceKey()).isEqualTo("my_jpeg_image_(1).jpg"); assertThat(imageInfo2.getResourceKey()).isEqualTo("my_jpeg_image_(1).jpg");
TbResourceInfo imageInfo3 = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE); TbResourceInfo imageInfo3 = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE, false);
assertThat(imageInfo3.getTitle()).isEqualTo(filename); assertThat(imageInfo3.getTitle()).isEqualTo(filename);
assertThat(imageInfo3.getFileName()).isEqualTo(filename); assertThat(imageInfo3.getFileName()).isEqualTo(filename);
assertThat(imageInfo3.getResourceKey()).isEqualTo("my_jpeg_image_(2).jpg"); assertThat(imageInfo3.getResourceKey()).isEqualTo("my_jpeg_image_(2).jpg");
@ -126,11 +127,11 @@ public class ImageControllerTest extends AbstractControllerTest {
@Test @Test
public void testUpdateImage() throws Exception { public void testUpdateImage() throws Exception {
String filename = "my_png_image.png"; String filename = "my_png_image.png";
TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE); TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false);
checkPngImageDescriptor(imageInfo.getDescriptor(ImageDescriptor.class)); checkPngImageDescriptor(imageInfo.getDescriptor(ImageDescriptor.class));
String newFilename = "my_jpeg_image.png"; String newFilename = "my_jpeg_image.png";
imageInfo = uploadImage(HttpMethod.PUT, "/api/images/tenant/" + filename, newFilename, "image/jpeg", JPEG_IMAGE); imageInfo = uploadImage(HttpMethod.PUT, "/api/images/tenant/" + filename, newFilename, "image/jpeg", JPEG_IMAGE, false);
assertThat(imageInfo.getTitle()).isEqualTo(filename); assertThat(imageInfo.getTitle()).isEqualTo(filename);
assertThat(imageInfo.getResourceKey()).isEqualTo(filename); assertThat(imageInfo.getResourceKey()).isEqualTo(filename);
@ -146,7 +147,7 @@ public class ImageControllerTest extends AbstractControllerTest {
@Test @Test
public void testUpdateImageInfo() throws Exception { public void testUpdateImageInfo() throws Exception {
String filename = "my_png_image.png"; String filename = "my_png_image.png";
TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE); TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false);
ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class); ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class);
assertThat(imageInfo.getTitle()).isEqualTo(filename); assertThat(imageInfo.getTitle()).isEqualTo(filename);
@ -165,7 +166,7 @@ public class ImageControllerTest extends AbstractControllerTest {
@Test @Test
public void testExportImportImage() throws Exception { public void testExportImportImage() throws Exception {
String filename = "my_png_image.png"; String filename = "my_png_image.png";
uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE); uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false);
ImageExportData exportData = doGet("/api/images/tenant/" + filename + "/export", ImageExportData.class); ImageExportData exportData = doGet("/api/images/tenant/" + filename + "/export", ImageExportData.class);
assertThat(exportData.getMediaType()).isEqualTo("image/png"); assertThat(exportData.getMediaType()).isEqualTo("image/png");
@ -188,11 +189,11 @@ public class ImageControllerTest extends AbstractControllerTest {
public void testGetImages() throws Exception { public void testGetImages() throws Exception {
loginSysAdmin(); loginSysAdmin();
String systemImageName = "my_system_png_image.png"; String systemImageName = "my_system_png_image.png";
TbResourceInfo systemImage = uploadImage(HttpMethod.POST, "/api/image", systemImageName, "image/png", PNG_IMAGE); TbResourceInfo systemImage = uploadImage(HttpMethod.POST, "/api/image", systemImageName, "image/png", PNG_IMAGE, false);
loginTenantAdmin(); loginTenantAdmin();
String tenantImageName = "my_jpeg_image.jpg"; String tenantImageName = "my_jpeg_image.jpg";
TbResourceInfo tenantImage = uploadImage(HttpMethod.POST, "/api/image", tenantImageName, "image/jpeg", JPEG_IMAGE); TbResourceInfo tenantImage = uploadImage(HttpMethod.POST, "/api/image", tenantImageName, "image/jpeg", JPEG_IMAGE, false);
List<TbResourceInfo> tenantImages = getImages(null, false, 10); List<TbResourceInfo> tenantImages = getImages(null, false, 10);
assertThat(tenantImages).containsOnly(tenantImage); assertThat(tenantImages).containsOnly(tenantImage);
@ -206,6 +207,40 @@ public class ImageControllerTest extends AbstractControllerTest {
.containsOnly(tenantImage); .containsOnly(tenantImage);
} }
@Test
public void testUploadPublicImage() throws Exception {
String filename = "my_public_image.png";
TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, true);
assertThat(imageInfo.isPublic()).isTrue();
assertThat(imageInfo.getPublicResourceKey()).hasSize(32);
assertThat(imageInfo.getLink()).isEqualTo("/api/images/public/" + imageInfo.getPublicResourceKey());
assertThat(downloadImage("tenant", filename)).containsExactly(PNG_IMAGE);
resetTokens();
assertThat(downloadPublicImage(imageInfo.getPublicResourceKey())).containsExactly(PNG_IMAGE);
}
@Test
public void testMakeImagePublic() throws Exception {
String filename = "my_public_image.png";
TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false);
assertThat(imageInfo.isPublic()).isFalse();
String publicKey = imageInfo.getPublicResourceKey();
assertThat(publicKey).hasSize(32);
doGet("/api/images/public/" + publicKey).andExpect(status().isNotFound());
imageInfo.setPublic(true);
imageInfo = doPut("/api/images/tenant/" + filename + "/public/true", imageInfo, TbResourceInfo.class);
resetTokens();
assertThat(downloadPublicImage(publicKey)).containsExactly(PNG_IMAGE);
loginTenantAdmin();
imageInfo.setPublic(false);
doPut("/api/images/tenant/" + filename + "/public/false", imageInfo, TbResourceInfo.class);
doGet("/api/images/public/" + publicKey).andExpect(status().isNotFound());
}
private void checkPngImageDescriptor(ImageDescriptor imageDescriptor) { private void checkPngImageDescriptor(ImageDescriptor imageDescriptor) {
assertThat(imageDescriptor.getMediaType()).isEqualTo("image/png"); assertThat(imageDescriptor.getMediaType()).isEqualTo("image/png");
assertThat(imageDescriptor.getWidth()).isEqualTo(200); assertThat(imageDescriptor.getWidth()).isEqualTo(200);
@ -266,10 +301,18 @@ public class ImageControllerTest extends AbstractControllerTest {
.andReturn().getResponse().getContentAsByteArray(); .andReturn().getResponse().getContentAsByteArray();
} }
private <R> TbResourceInfo uploadImage(HttpMethod httpMethod, String url, String filename, String mediaType, byte[] content) throws Exception { private byte[] downloadPublicImage(String publicKey) throws Exception {
return doGet("/api/images/public/" + publicKey).andExpect(status().isOk())
.andReturn().getResponse().getContentAsByteArray();
}
private <R> TbResourceInfo uploadImage(HttpMethod httpMethod, String url, String filename, String mediaType, byte[] content, boolean isPublic) throws Exception {
MockMultipartFile file = new MockMultipartFile("file", filename, mediaType, content); MockMultipartFile file = new MockMultipartFile("file", filename, mediaType, content);
MockMultipartHttpServletRequestBuilder request = MockMvcRequestBuilders.multipart(httpMethod, url); MockPart publicPart = new MockPart("isPublic", String.valueOf(isPublic).getBytes());
request.file(file); publicPart.getHeaders().setContentType(MediaType.APPLICATION_JSON);
var request = MockMvcRequestBuilders.multipart(httpMethod, url)
.file(file)
.part(publicPart);
setJwtToken(request); setJwtToken(request);
return readResponse(mockMvc.perform(request).andExpect(status().isOk()), TbResourceInfo.class); return readResponse(mockMvc.perform(request).andExpect(status().isOk()), TbResourceInfo.class);
} }

39
dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java

@ -20,8 +20,10 @@ import com.drew.metadata.Directory;
import com.drew.metadata.Metadata; import com.drew.metadata.Metadata;
import com.drew.metadata.Tag; import com.drew.metadata.Tag;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.With;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.batik.anim.dom.SAXSVGDocumentFactory; import org.apache.batik.anim.dom.SAXSVGDocumentFactory;
import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.BridgeContext;
@ -121,7 +123,7 @@ public class ImageUtils {
image.setHeight(bufferedImage.getHeight()); image.setHeight(bufferedImage.getHeight());
ProcessedImage preview = new ProcessedImage(); ProcessedImage preview = new ProcessedImage();
int[] thumbnailDimensions = getThumbnailDimensions(image.getWidth(), image.getHeight(), thumbnailMaxDimension); int[] thumbnailDimensions = getThumbnailDimensions(image.getWidth(), image.getHeight(), thumbnailMaxDimension, true);
preview.setWidth(thumbnailDimensions[0]); preview.setWidth(thumbnailDimensions[0]);
preview.setHeight(thumbnailDimensions[1]); preview.setHeight(thumbnailDimensions[1]);
@ -191,8 +193,18 @@ public class ImageUtils {
image.setSize(data.length); image.setSize(data.length);
PNGTranscoder transcoder = new PNGTranscoder(); PNGTranscoder transcoder = new PNGTranscoder();
transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_WIDTH, (float) thumbnailMaxDimension); if (image.getSize() < 10240) { // if SVG is smaller than 10kB (average 250x250 PNG preview size)
transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_HEIGHT, (float) thumbnailMaxDimension); return withPreviewAsOriginalImage(image);
} else if (image.getSize() > 102400 && image.getWidth() != 0) { // considering SVG image detailed after 100kB
// increasing preview dimensions
thumbnailMaxDimension = 512;
int[] thumbnailDimensions = getThumbnailDimensions(image.getWidth(), image.getHeight(), thumbnailMaxDimension, false);
transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) thumbnailDimensions[0]);
transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) thumbnailDimensions[1]);
} else {
transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_WIDTH, (float) thumbnailMaxDimension);
transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_HEIGHT, (float) thumbnailMaxDimension);
}
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
transcoder.transcode(new TranscoderInput(new ByteArrayInputStream(data)), new TranscoderOutput(out)); transcoder.transcode(new TranscoderInput(new ByteArrayInputStream(data)), new TranscoderOutput(out));
byte[] pngThumbnail = out.toByteArray(); byte[] pngThumbnail = out.toByteArray();
@ -214,18 +226,16 @@ public class ImageUtils {
image.setSize(data.length); image.setSize(data.length);
image.setWidth(0); image.setWidth(0);
image.setHeight(0); image.setHeight(0);
ProcessedImage preview = new ProcessedImage(); return withPreviewAsOriginalImage(image);
preview.setMediaType(mediaType); }
preview.setData(null);
preview.setSize(data.length); public static ProcessedImage withPreviewAsOriginalImage(ProcessedImage originalImage) {
preview.setWidth(0); originalImage.setPreview(originalImage.withData(null));
preview.setHeight(0); return originalImage;
image.setPreview(preview);
return image;
} }
private static int[] getThumbnailDimensions(int originalWidth, int originalHeight, int maxDimension) { private static int[] getThumbnailDimensions(int originalWidth, int originalHeight, int maxDimension, boolean originalIfSmaller) {
if (originalWidth <= maxDimension && originalHeight <= maxDimension) { if (originalWidth <= maxDimension && originalHeight <= maxDimension && originalIfSmaller) {
return new int[]{originalWidth, originalHeight}; return new int[]{originalWidth, originalHeight};
} }
int thumbnailWidth; int thumbnailWidth;
@ -242,10 +252,13 @@ public class ImageUtils {
} }
@Data @Data
@AllArgsConstructor
@NoArgsConstructor
public static class ProcessedImage { public static class ProcessedImage {
private String mediaType; private String mediaType;
private int width; private int width;
private int height; private int height;
@With
private byte[] data; private byte[] data;
private long size; private long size;
private ProcessedImage preview; private ProcessedImage preview;

Loading…
Cancel
Save