Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Feat: handle heic files #882

Merged
merged 4 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Aptfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
ttf-mscorefonts-installer
tesseract-ocr
tesseract-ocr
imagemagick
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class ExtensionValidator implements ConstraintValidator<Extension, Docume
private static final List<String> contentTypes = Arrays.asList(
"image/png",
"image/jpeg",
"image/heif",
"application/pdf"
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import fr.dossierfacile.common.enums.FileStatus;
import fr.dossierfacile.common.enums.FileStorageStatus;
import fr.dossierfacile.common.repository.WatermarkDocumentRepository;
import fr.dossierfacile.common.service.interfaces.DocumentHelperService;
import fr.dossierfacile.common.service.interfaces.EncryptionKeyService;
import fr.dossierfacile.common.service.interfaces.FileStorageService;
import jakarta.servlet.http.HttpServletResponse;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class DocumentServiceImpl implements DocumentService {
private final FileStorageService fileStorageService;
private final EncryptionKeyService encryptionKeyService;
private final WatermarkDocumentRepository watermarkDocumentRepository;
private final DocumentHelperService documentHelperService;

@Override
@Transactional
Expand Down Expand Up @@ -152,7 +154,20 @@ private WatermarkDocument createDocument(DocumentForm documentForm) {
.build();

try {
return fileStorageService.upload(multipartFile.getInputStream(), file);
if ("image/heif".equals(multipartFile.getContentType())) {
file.setName(multipartFile.getOriginalFilename().replaceAll("(?i)\\.heic$", "") + ".jpg");
file.setContentType("image/jpeg");

InputStream jpgInputStream = documentHelperService.convertHeicToJpg(multipartFile.getInputStream());
if (jpgInputStream != null) {
return fileStorageService.upload(jpgInputStream, file);
} else {
throw new IOException("Image could not be saved");
}
} else {
return fileStorageService.upload(multipartFile.getInputStream(), file);
}

} catch (IOException e) {
log.error("Error on file save", e);
throw new RuntimeException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package fr.dossierfacile.common.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ImageMagickConfig {
@Value("${path.imagemagick.cli:/usr/bin/convert}")
private String imageMagickCli;

public String getImageMagickCli() {
return imageMagickCli;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fr.dossierfacile.common.service;

import fr.dossierfacile.common.config.ImageMagickConfig;
import fr.dossierfacile.common.entity.Document;
import fr.dossierfacile.common.entity.File;
import fr.dossierfacile.common.entity.StorageFile;
Expand All @@ -11,13 +12,14 @@
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

Expand All @@ -26,9 +28,15 @@
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.List;
import java.util.Set;
import java.util.UUID;

@Service
Expand All @@ -38,19 +46,35 @@ public class DocumentHelperServiceImpl implements DocumentHelperService {
private final FileStorageService fileStorageService;
private final SharedFileRepository fileRepository;
private final EncryptionKeyService encryptionKeyService;
private final ImageMagickConfig imageMagickConfig;

@Transactional
@Override
public File addFile(MultipartFile multipartFile, Document document) throws IOException {

StorageFile storageFile = StorageFile.builder()
.name(multipartFile.getOriginalFilename())
.contentType(multipartFile.getContentType())
.size(multipartFile.getSize())
.encryptionKey(encryptionKeyService.getCurrentKey())
.build();
String originalFilename = multipartFile.getOriginalFilename();
if (originalFilename == null) {
originalFilename = UUID.randomUUID().toString();
}
if ("image/heif".equals(multipartFile.getContentType())) {
storageFile.setName(originalFilename.replaceAll("(?i)\\.heic$", "") + ".jpg");
storageFile.setContentType("image/jpeg");

InputStream jpgInputStream = convertHeicToJpg(multipartFile.getInputStream());
if (jpgInputStream != null) {
storageFile = fileStorageService.upload(jpgInputStream, storageFile);
} else {
throw new IOException("Image could not be saved");
}
} else {
storageFile.setName(originalFilename);
storageFile.setContentType(multipartFile.getContentType());
storageFile = fileStorageService.upload(multipartFile.getInputStream(), storageFile);
}

storageFile = fileStorageService.upload(multipartFile.getInputStream(), storageFile);

File file = File.builder()
.storageFile(storageFile)
Expand All @@ -64,6 +88,43 @@ public File addFile(MultipartFile multipartFile, Document document) throws IOExc
return file;
}

@Override
public InputStream convertHeicToJpg(InputStream heicInputStream) throws IOException {
String tmpImageName = UUID.randomUUID().toString();
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"));
java.io.File heicFile = java.nio.file.Files.createTempFile(tmpImageName, ".heic", attr).toFile();

try (FileOutputStream fileOutputStream = new FileOutputStream(heicFile)) {
IOUtils.copy(heicInputStream, fileOutputStream);
}

java.io.File jpgFile = java.nio.file.Files.createTempFile(tmpImageName, ".jpg", attr).toFile();

// Use ImageMagick to convert .heic to .jpg
ProcessBuilder processBuilder = new ProcessBuilder(imageMagickConfig.getImageMagickCli(), heicFile.getAbsolutePath(), jpgFile.getAbsolutePath());
Process process = processBuilder.start();
try {
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("Erreur lors de la conversion HEIC en JPG avec ImageMagick.");
}
} catch (InterruptedException e) {
log.error("Interrupted exception", e);
Thread.currentThread().interrupt();
throw new IOException(e);
}

InputStream jpgInputStream = new FileInputStream(jpgFile);

boolean delete = heicFile.delete();
if (!delete) {
log.error("Could not delete temporary file");
}
jpgFile.deleteOnExit();

return jpgInputStream;
}

@Override
public void deleteFiles(Document document) {
if (document.getFiles() != null && !document.getFiles().isEmpty()) {
Expand All @@ -84,7 +145,7 @@ public StorageFile generatePreview(InputStream fileInputStream, String originalN
PDFRenderer pdfRenderer = new PDFRenderer(document);
BufferedImage bufferedImage = pdfRenderer.renderImageWithDPI(0, 200, ImageType.RGB);
preview = resizeImage(bufferedImage);
log.info("resize pdf duration : " + (System.currentTimeMillis() - startTime));
log.info("resize pdf duration : {}", System.currentTimeMillis() - startTime);
}
} else {
preview = resizeImage(ImageIO.read(fileInputStream));
Expand Down Expand Up @@ -129,7 +190,7 @@ BufferedImage resizeImage(BufferedImage image) {
graphics2D.dispose();
long endTime = System.currentTimeMillis();
long duration = (endTime - startTime);
log.info("resize image duration : " + duration);
log.info("resize image duration : {}", duration);
return resizedImage;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public interface DocumentHelperService {
*/
File addFile(MultipartFile multipartFile, Document document) throws IOException;

InputStream convertHeicToJpg(InputStream heicInputStream) throws IOException;

/**
* Delete files contained in document.
*/
Expand Down
Loading