Commit eeb3df10 authored by Matija Obreza's avatar Matija Obreza

Create thumbnails of pre-determined sizes when images added to Gallery

- Delete thumbnails when RepositoryImage removed
parent 3b2ad713
Pipeline #7259 failed with stage
in 3 minutes and 27 seconds
......@@ -83,6 +83,13 @@ public interface ImageGalleryService {
* @return the image gallery
*/
ImageGallery saveImageOrder(ImageGallery imageGallery);
/**
* Ensure that thumbnails are created for pre-configured thumbnail sizes
*
* @param imageGallery
*/
void ensureThumbnails(ImageGallery imageGallery);
/**
* Ensure that thumbnails of images in the gallery exist in the repository at
......
......@@ -80,6 +80,8 @@ public class ImageGalleryServiceImpl implements ImageGalleryService {
@Autowired
private JPAQueryFactory jpaQueryFactory;
private int[] thumbnailSizes = { 200 };
/*
* (non-Javadoc)
* @see
......@@ -148,7 +150,9 @@ public class ImageGalleryServiceImpl implements ImageGalleryService {
imageGallery.setImages(images);
return imageGalleryPersistence.save(imageGallery);
imageGallery = imageGalleryPersistence.save(imageGallery);
ensureThumbnails(imageGallery);
return imageGallery;
}
/*
......@@ -186,7 +190,7 @@ public class ImageGalleryServiceImpl implements ImageGalleryService {
* @param image the image
* @return the full thumbnails path
*/
private Path getFullThumbnailsPath(final RepositoryImage image) {
public static Path getFullThumbnailsPath(final RepositoryImage image) {
return Paths.get(THUMB_PATH, image.getThumbnailPath());
}
......@@ -225,8 +229,42 @@ public class ImageGalleryServiceImpl implements ImageGalleryService {
imageGallery2.setImages(imageGallery.getImages());
imageGalleryPersistence.save(imageGallery2);
ensureThumbnails(imageGallery);
return deepLoad(imageGallery2);
}
@Override
public void ensureThumbnails(final ImageGallery imageGallery) {
final ImageGallery imageGallery2 = imageGalleryPersistence.findOne(imageGallery.getId());
if (CollectionUtils.isEmpty(imageGallery2.getImages())) {
LOG.debug("ImageGallery has no images, skipping thumbnail generation for path=" + imageGallery2.getPath());
return;
}
imageGallery2.getImages().forEach(repositoryImage -> {
try {
final byte[][] cache = new byte[1][];
for (int thumbSize: thumbnailSizes) {
ensureThumbnail(thumbSize, thumbSize, repositoryImage, () -> {
if (cache[0] != null) {
LOG.warn("Using cached image bytes for {}", repositoryImage.getPath());
return cache[0];
} else {
LOG.warn("Must load image bytes for {}", repositoryImage.getPath());
return cache[0] = bytesStorageService.get(repositoryImage.storagePath());
}
});
}
} catch (final NullPointerException e) {
LOG.error("Error generating thumbnail for image={} message={}", repositoryImage, e.getMessage(), e);
} catch (final Exception e) {
LOG.error("Error generating thumbnail for " + repositoryImage, e);
}
});
}
/**
* For each image in the gallery, generate a PNG thumbnail at maximum width x
......@@ -238,6 +276,7 @@ public class ImageGalleryServiceImpl implements ImageGalleryService {
* @param imageGallery the image gallery
* @param width the width
* @param height the height
* @deprecated Use {@link #ensureThumbnails(ImageGallery)} instead.
*/
@Override
public void ensureThumbnails(final ImageGallery imageGallery, final Integer width, final Integer height) {
......@@ -251,7 +290,9 @@ public class ImageGalleryServiceImpl implements ImageGalleryService {
imageGallery2.getImages().forEach(repositoryImage -> {
try {
ensureThumbnail(width, height, repositoryImage);
ensureThumbnail(width, height, repositoryImage, () -> {
return bytesStorageService.get(repositoryImage.storagePath());
});
} catch (final NullPointerException e) {
LOG.error("Error generating thumbnail for image={} message={}", repositoryImage, e.getMessage(), e);
} catch (final Exception e) {
......@@ -270,21 +311,23 @@ public class ImageGalleryServiceImpl implements ImageGalleryService {
* @throws IOException Signals that an I/O exception has occurred.
* @throws InvalidRepositoryPathException if path is messed up
*/
private void ensureThumbnail(final Integer width, final Integer height, final RepositoryImage repositoryImage) throws IOException, InvalidRepositoryPathException {
private void ensureThumbnail(final Integer width, final Integer height, final RepositoryImage repositoryImage, final IImageBytesProvider loader) throws IOException, InvalidRepositoryPathException {
final String filename = getThumbnailFilename(width, height, repositoryImage.getUuid());
if (!bytesStorageService.exists(getFullThumbnailsPath(repositoryImage).resolve(filename))) {
if (LOG.isDebugEnabled()) {
LOG.debug("Generating new thumbnail width={} height={} for image={}", width, height, repositoryImage.getUuid());
}
final byte[] bytesPng = thumbnailGenerator.createThumbnail(width, height, bytesStorageService.get(repositoryImage.storagePath()));
LOG.debug("Generating new thumbnail width={} height={} for image={}", width, height, repositoryImage.getUuid());
if (LOG.isDebugEnabled()) {
try {
final byte[] bytesPng = thumbnailGenerator.createThumbnail(width, height, loader.getImageBytes());
LOG.debug("Persisting new thumbnail width={} height={} for image={}", width, height, repositoryImage.getUuid());
}
bytesStorageService.upsert(getFullThumbnailsPath(repositoryImage).resolve(filename), bytesPng);
bytesStorageService.upsert(getFullThumbnailsPath(repositoryImage).resolve(filename), bytesPng);
} catch (NullPointerException e) {
LOG.warn("Error generating thumbnail: {}", e.getMessage());
}
}
}
......@@ -348,4 +391,17 @@ public class ImageGalleryServiceImpl implements ImageGalleryService {
// content.forEach(imageGallery -> imageGallery.getFolder().getId());
return new PageImpl<>(content, pageable, total);
}
public void setThumbnailSizes(int[] thumbnailSizes) {
this.thumbnailSizes = thumbnailSizes;
}
/**
* Helper to retrive image bytes (and use loaded bytes when possible).
*
* @author Matija Obreza
*/
private static interface IImageBytesProvider {
byte[] getImageBytes() throws IOException;
}
}
......@@ -586,6 +586,22 @@ public class RepositoryServiceImpl implements RepositoryService, InitializingBea
}
bytesStorageService.remove(repositoryImage.storagePath());
Path path = ImageGalleryServiceImpl.getFullThumbnailsPath(repositoryImage);
try {
List<String> thumbnails = bytesStorageService.listFiles(path);
for (String thumb : thumbnails) {
System.err.println("Should remove " + thumb + " from " + path.resolve(thumb));
try {
bytesStorageService.remove(path.resolve(thumb));
} catch (IOException e) {
LOG.warn("Trouble removing thumbnail: {}", e.getMessage());
}
}
} catch (InvalidRepositoryPathException e) {
LOG.warn("Trouble removing thumbnails: {}", e.getMessage());
}
repositoryImagePersistence.delete(repositoryImage);
return repositoryImage;
}
......
......@@ -121,14 +121,12 @@ public class ImageGalleryThumbnailsTest extends RepositoryServiceTest {
final TestImage image1 = new TestImage("maize.jpg", "image/jpg");
final RepositoryImage repoImage1 = fileRepoService.addImage(initialPath, image1.getOriginalFilename(), image1.getContentType(), image1.getImageBytes(), null);
imageGalleryService.ensureThumbnails(imageGallery, 100, 200);
final byte[] thumbBytes1 = bytesStorageService.get(Paths.get(ImageGalleryService.THUMB_PATH, repoImage1.getThumbnailPath(), "100x200" + ".png"));
final byte[] thumbBytes1 = bytesStorageService.get(Paths.get(ImageGalleryService.THUMB_PATH, repoImage1.getThumbnailPath(), "200x200" + ".png"));
assertThat("Thumbnail must not be null", thumbBytes1, notNullValue());
try (InputStream is = new ByteArrayInputStream(thumbBytes1)) {
final BufferedImage imageData = ImageIO.read(is);
assertThat("Thumbnail width mismatch", imageData.getWidth(), equalTo(100));
assertThat("Thumbnail width mismatch", imageData.getWidth(), equalTo(200));
assertThat("Thumbnail height mismatch", imageData.getHeight(), equalTo(200));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment