Created
October 27, 2024 00:42
-
-
Save prmichaelsen/d82304f07427ec6cce87ea16ee9b4917 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Log4j2 | |
public class ImageUtility { | |
public MultipartFile removeExifAndApplyOrientation(MultipartFile file, String mimeType) throws Exception { | |
final BufferedImage image = ImageIO.read(file.getInputStream()); | |
final Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); | |
final Path originalFile = Files.createTempFile(tempDir, "original-image-", ""); | |
final String originalPath = originalFile.toAbsolutePath().toString(); | |
final ByteArrayOutputStream originalBytes = new ByteArrayOutputStream(); | |
// jpg is lossy, and in this case when we try to write the file out, | |
// the ICCProfile is lost. Therefore, if it's a jpg, we use the lossless | |
// tiff format to write to file so we can then copy the color profile later. | |
// https://stackoverflow.com/a/1271751/5013193 | |
// https://stackoverflow.com/a/21219913/5013193 | |
ImageIO.write(image, this.getType(mimeType) == "jpg" ? "tiff" : "png", originalBytes); | |
Files.write(originalFile, originalBytes.toByteArray()); | |
final Path tempFile = Files.createTempFile(tempDir, "image-", ""); | |
final String tempPath = tempFile.toAbsolutePath().toString(); | |
final ByteArrayOutputStream tempBytes = new ByteArrayOutputStream(); | |
ImageIO.write(image, this.getType(mimeType), tempBytes); | |
Files.write(tempFile, tempBytes.toByteArray()); | |
final int orientation = this.getOrientation(file, mimeType); | |
if (orientation != 1) { | |
String degrees; | |
boolean mirror = false; | |
switch (orientation) { | |
case 2: | |
mirror = true; | |
degrees = "0"; | |
break; | |
case 3: | |
degrees = "180"; | |
break; | |
case 4: | |
mirror = true; | |
degrees = "180"; | |
break; | |
case 5: | |
mirror = true; | |
degrees = "90"; | |
break; | |
case 6: | |
degrees = "90"; | |
break; | |
case 7: | |
mirror = true; | |
degrees = "270"; | |
break; | |
case 8: | |
degrees = "270"; | |
break; | |
default: | |
degrees = "0"; | |
break; | |
} | |
String[] magickCommand; | |
if (mirror) { | |
magickCommand = new String[] { | |
"/apollo/env/GestaltBuyerExperience/bin/magick-script", | |
"convert", tempPath, | |
"-rotate", degrees, | |
"-flop", | |
tempPath, | |
}; | |
} else { | |
magickCommand = new String[] { | |
"/apollo/env/GestaltBuyerExperience/bin/magick-script", | |
"convert", tempPath, | |
"-rotate", degrees, | |
tempPath, | |
}; | |
} | |
final Process magickProcess = Runtime.getRuntime().exec(magickCommand); | |
magickProcess.waitFor(); | |
} | |
final String[] exifCommand = { | |
"/apollo/env/GestaltBuyerExperience/bin/exiftool/exiftool", | |
"-gps:all=", | |
"-orientation=", | |
"-overwrite_original", | |
tempPath, | |
}; | |
final Process exifProcess = Runtime.getRuntime().exec(exifCommand); | |
exifProcess.waitFor(); | |
// https://exiftool.org/exiftool_pod.html#exiftool--TagsFromFile-src.jpg--icc_profile-dst.jpg | |
String[] copyProfileCommand = { | |
"/apollo/env/GestaltBuyerExperience/bin/exiftool/exiftool", | |
"-TagsFromFile", | |
originalPath, | |
"-icc_profile", | |
tempPath, | |
}; | |
final Process copyProfileProcess = Runtime.getRuntime().exec(copyProfileCommand); | |
copyProfileProcess.waitFor(); | |
final byte[] bytes = Files.readAllBytes(tempFile); | |
final MultipartFile resultImageFile = new MockMultipartFile(file.getName(), | |
file.getOriginalFilename(), file.getContentType(), bytes); | |
Files.delete(originalFile); | |
Files.delete(tempFile); | |
return resultImageFile; | |
} | |
private int getOrientation(MultipartFile file, String mimeType) throws Exception { | |
switch (mimeType) { | |
case "image/jpeg": | |
return this.getJpgOrientation(file); | |
case "image/png": | |
return this.getPngOrientation(file); | |
default: | |
throw new Exception("File type not valid"); | |
} | |
} | |
private int getJpgOrientation(MultipartFile file) throws Exception { | |
final Metadata metadata = ImageMetadataReader.readMetadata(file.getInputStream()); | |
final Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); | |
final JpegDirectory jpegDirectory = metadata.getFirstDirectoryOfType(JpegDirectory.class); | |
int orientation = 1; | |
try { | |
if (directory != null) { | |
orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION); | |
} | |
} catch (MetadataException me) { | |
log.warn("Could not get orientation"); | |
} | |
return orientation; | |
} | |
private int getPngOrientation(MultipartFile file) throws Exception { | |
final Metadata metadata = ImageMetadataReader.readMetadata(file.getInputStream()); | |
final Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); | |
int orientation = 1; | |
try { | |
if (directory != null) { | |
orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION); | |
} | |
} catch (MetadataException me) { | |
log.warn("Could not get orientation"); | |
} | |
return orientation; | |
} | |
private String getType(String mimeType) throws Exception { | |
switch (mimeType) { | |
case "image/jpeg": | |
return "jpg"; | |
case "image/png": | |
return "png"; | |
default: | |
throw new Exception("File type not valid"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment