Skip to content

Commit

Permalink
Merge branch '2.0-M4-maintenance' into 2.0-M4-maintenance-google-wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
cbellone committed Apr 7, 2023
2 parents 59ea571 + daea776 commit 5967bfe
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@
@Log4j2
public class FileUploadApiController {

private static final int IMAGE_THUMB_MAX_WIDTH_PX = 500;
private static final int IMAGE_THUMB_MAX_HEIGHT_PX = 500;

private final FileUploadManager fileUploadManager;

@Autowired
Expand All @@ -50,36 +47,12 @@ public FileUploadApiController(FileUploadManager fileUploadManager) {
}

@PostMapping("/file/upload")
public ResponseEntity<String> uploadFile(@RequestParam(required = false, value = "resizeImage", defaultValue = "false") Boolean resizeImage,
@RequestBody UploadBase64FileModification upload) {

public ResponseEntity<String> uploadFile(@RequestBody UploadBase64FileModification upload) {
try {
final var mimeType = MimeTypeUtils.parseMimeType(upload.getType());
if (Boolean.TRUE.equals(resizeImage)) {
upload = resize(upload, mimeType);
}
return ResponseEntity.ok(fileUploadManager.insertFile(upload));
} catch (Exception e) {
log.error("error while uploading image", e);
return ResponseEntity.badRequest().build();
}
}

private UploadBase64FileModification resize(UploadBase64FileModification upload, MimeType mimeType) throws IOException {
BufferedImage image = ImageIO.read(new ByteArrayInputStream(upload.getFile()));
//resize only if the image is bigger than 500px on one of the side
if (image.getWidth() > IMAGE_THUMB_MAX_WIDTH_PX || image.getHeight() > IMAGE_THUMB_MAX_HEIGHT_PX) {
UploadBase64FileModification resized = new UploadBase64FileModification();
BufferedImage thumbImg = Scalr.resize(image, Scalr.Method.QUALITY, Scalr.Mode.AUTOMATIC, IMAGE_THUMB_MAX_WIDTH_PX, IMAGE_THUMB_MAX_HEIGHT_PX, Scalr.OP_ANTIALIAS);
try (final var baos = new ByteArrayOutputStream()) {
ImageIO.write(thumbImg, mimeType.getSubtype(), baos);
resized.setFile(baos.toByteArray());
}
resized.setAttributes(upload.getAttributes());
resized.setName(upload.getName());
resized.setType(upload.getType());
return resized;
}
return upload;
}
}
2 changes: 1 addition & 1 deletion src/main/java/alfio/manager/FileDownloadManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public DownloadedFile downloadFile(String url) {
if(callSuccessful(response)) {
String[] parts = Pattern.compile("/").split(url);
String name = parts[parts.length - 1];
if(Objects.nonNull(response.body()) && response.body().length <= FileUploadManager.MAXIMUM_ALLOWED_SIZE) {
if(Objects.nonNull(response.body())) {
return new DownloadedFile(
response.body(),
name,
Expand Down
68 changes: 57 additions & 11 deletions src/main/java/alfio/manager/FileUploadManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,22 @@
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.imgscalr.Scalr;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.time.Duration;
import java.util.*;

Expand All @@ -42,22 +47,33 @@
@RequiredArgsConstructor
public class FileUploadManager {

static final int IMAGE_THUMB_MAX_WIDTH_PX = 300;
static final int IMAGE_THUMB_MAX_HEIGHT_PX = 200;
/**
* Maximum allowed file size is 200kb
*/
static final int MAXIMUM_ALLOWED_SIZE = 1024 * 200;
private static final int MAXIMUM_ALLOWED_SIZE = 1024 * 200;
private static final MimeType IMAGE_TYPE = MimeType.valueOf("image/*");
private final FileUploadRepository repository;
private final Cache<String, File> cache = Caffeine.newBuilder()
.maximumSize(20)
.expireAfterWrite(Duration.ofMinutes(20))
.removalListener((String key, File value, RemovalCause cause) -> {
if(value != null) {
boolean result = value.delete();
log.trace("deleted {}: {}", key, result);
}
})
.removalListener(removalListener())
.build();

private static RemovalListener<String, File> removalListener() {
return (String key, File value, RemovalCause cause) -> {
if (value != null) {
try {
Files.delete(value.toPath());
log.trace("deleted {}", key);
} catch(Exception ex) {
log.trace("Error while deleting file", ex);
}
}
};
}

public Optional<FileBlobMetadata> findMetadata(String id) {
return repository.findById(id);
}
Expand All @@ -79,12 +95,13 @@ public void outputFile(String id, OutputStream out) {
}
}


public String insertFile(UploadBase64FileModification file) {
Validate.exclusiveBetween(1, MAXIMUM_ALLOWED_SIZE, file.getFile().length);
String digest = DigestUtils.sha256Hex(file.getFile());
final var mimeType = MimeTypeUtils.parseMimeType(file.getType());
var upload = resizeIfNeeded(file, mimeType);
Validate.exclusiveBetween(1, MAXIMUM_ALLOWED_SIZE, upload.getFile().length);
String digest = DigestUtils.sha256Hex(upload.getFile());
if (Integer.valueOf(0).equals(repository.isPresent(digest))) {
repository.upload(file, digest, getAttributes(file));
repository.upload(upload, digest, getAttributes(upload));
}
return digest;
}
Expand All @@ -94,6 +111,35 @@ public void cleanupUnreferencedBlobFiles(Date date) {
log.debug("removed {} unused file_blob", deleted);
}

/**
* @author <a href="https://github.com/emassip">Etienne M.</a>
*/
private UploadBase64FileModification resizeIfNeeded(UploadBase64FileModification upload, MimeType mimeType) {
if (!mimeType.isCompatibleWith(IMAGE_TYPE)) {
// not an image, nothing to do here.
return upload;
}
try {
BufferedImage image = ImageIO.read(new ByteArrayInputStream(upload.getFile()));
// resize only if the image is bigger than target size on either side
if (image.getWidth() > IMAGE_THUMB_MAX_WIDTH_PX || image.getHeight() > IMAGE_THUMB_MAX_HEIGHT_PX) {
UploadBase64FileModification resized = new UploadBase64FileModification();
BufferedImage thumbImg = Scalr.resize(image, Scalr.Method.QUALITY, Scalr.Mode.AUTOMATIC, IMAGE_THUMB_MAX_WIDTH_PX, IMAGE_THUMB_MAX_HEIGHT_PX, Scalr.OP_ANTIALIAS);
try (final var baos = new ByteArrayOutputStream()) {
ImageIO.write(thumbImg, mimeType.getSubtype(), baos);
resized.setFile(baos.toByteArray());
}
resized.setAttributes(upload.getAttributes());
resized.setName(upload.getName());
resized.setType(upload.getType());
return resized;
}
return upload;
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}

private Map<String, String> getAttributes(UploadBase64FileModification file) {
if(!StringUtils.startsWith(file.getType(), "image/")) {
return Collections.emptyMap();
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/alfio/i18n/public_it.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
breadcrumb.step1 = Biglietti
breadcrumb.step1 = Seleziona
breadcrumb.step2 = Dati di Contatto
breadcrumb.step3 = Pagamento
breadcrumb.step3.free = Sommario
Expand Down Expand Up @@ -287,6 +287,7 @@ reservation-page-complete.phone-number = Numero di telefono
reservation-page-complete.please-check-input-fields = Controlla i valori immessi.
reservation-page-complete.release-button.text = Rinuncia
reservation-page-complete.resend-reservation-email = Invia email prenotazione
reservation-page-complete.reservation.finalization-in-progress = La prenotazione è in fase di conferma. Riceverai una mail non appena il processo è completo.
reservation-page-complete.send-ticket-by-email-to = Invia
reservation-page-complete.show-ticket = Visualizza
reservation-page-complete.subscription = Dettagli Abbonamento
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ <h3>Logo</h3>
<div class="form-group">
<label for="imageFile">Image</label>
<div id="imageFile" class="drop-file-zone wMarginBottom well" data-accept="image/*" data-ngf-pattern="'image/*'" data-ng-model="droppedFile" data-ngf-drop data-ngf-select data-ngf-multiple="false" data-ngf-allow-dir="false" data-ngf-drag-over-class="'drop-file-zone-hover'">
Drop image here or click to upload (Maximum size : 200KB)
Drop image here or click to upload (Maximum size : 1MB)
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ <h3>Image</h3>
data-accept="image/*" data-ngf-pattern="'image/*'"
data-ng-model="$ctrl.droppedFile"
data-ngf-drop data-ngf-select data-ngf-multiple="false" data-ngf-allow-dir="false" data-ngf-drag-over-class="'drop-file-zone-hover'">
Drop image here or click to upload (Maximum size : 200KB)
Drop image here or click to upload (Maximum size : 1MB)
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/main/webapp/resources/js/admin/service/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -997,8 +997,8 @@
deferred.reject('Your image was not uploaded correctly.Please upload the image again');
} else if (!((files[0].type === 'image/png') || (files[0].type === 'image/jpeg') || (files[0].type === 'image/gif') || (files[0].type === 'image/svg+xml'))) {
deferred.reject('Only PNG, JPG, GIF or SVG image files are accepted');
} else if (files[0].size > (1024 * 200)) {
deferred.reject('Image size exceeds the allowable limit 200KB');
} else if (files[0].size > (1024 * 1024)) {
deferred.reject('Image is too big');
} else {
reader.readAsDataURL(files[0]);
}
Expand Down
33 changes: 28 additions & 5 deletions src/test/java/alfio/manager/FileUploadManagerIntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@
import org.apache.commons.lang3.time.DateUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Date;
import java.util.Objects;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
Expand All @@ -50,7 +51,7 @@ class FileUploadManagerIntegrationTest extends BaseIntegrationTest {
private static final byte[] FILE = {1,2,3,4};

@Test
public void testInsert() {
void testInsert() {
UploadBase64FileModification toInsert = new UploadBase64FileModification();
toInsert.setFile(FILE);
toInsert.setName("myfile.txt");
Expand All @@ -76,7 +77,7 @@ public void testInsert() {


@Test
public void testInsertImage() {
void testInsertImage() {
UploadBase64FileModification toInsert = new UploadBase64FileModification();
toInsert.setFile(ONE_PIXEL_BLACK_GIF);
toInsert.setName("image.gif");
Expand All @@ -95,7 +96,29 @@ public void testInsertImage() {
}

@Test
public void testFindMetadataNotPresent() {
void testFindMetadataNotPresent() {
assertFalse(fileUploadManager.findMetadata("unknownid").isPresent());
}

@Test
void testInsertResizedImage() throws IOException {
// Image credit: NASA, ESA, CSA, and STScI
try (var in = getClass().getResourceAsStream("/images/main_image_star-forming_region_carina_reduced.jpg")) {
UploadBase64FileModification toInsert = new UploadBase64FileModification();
toInsert.setFile(Objects.requireNonNull(in).readAllBytes());
toInsert.setName("image.jpg");
toInsert.setType("image/jpeg");
String id = fileUploadManager.insertFile(toInsert);

Optional<FileBlobMetadata> metadata = fileUploadManager.findMetadata(id);

assertTrue(metadata.isPresent());

assertEquals(String.valueOf(FileUploadManager.IMAGE_THUMB_MAX_WIDTH_PX), metadata.get().getAttributes().get("width"));
assertEquals("174", metadata.get().getAttributes().get("height"));

fileUploadManager.cleanupUnreferencedBlobFiles(DateUtils.addDays(new Date(), 1));
assertFalse(fileUploadManager.findMetadata(id).isPresent());
}
}
}
1 change: 1 addition & 0 deletions src/test/resources/images/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Image credit: NASA, ESA, CSA, and STScI
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 5967bfe

Please sign in to comment.